Merge "Allow gradle benchmarks to run without downloading"
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 4a26889..39bdd96 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
+import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -135,6 +136,9 @@
if (options.hasMarker()) {
return options.getMarker();
}
+ if (options.testing.dontCreateMarkerInD8) {
+ return null;
+ }
Marker marker = new Marker(Tool.D8)
.setVersion(Version.LABEL)
.setMinApi(options.minApiLevel);
@@ -163,7 +167,14 @@
options.methodsFilter.forEach((m) -> System.out.println(" - " + m));
}
Marker marker = getMarker(options);
- new ApplicationWriter(app, options, marker, null, NamingLens.getIdentityLens(), null, null)
+ new ApplicationWriter(
+ app,
+ options,
+ marker == null ? null : Collections.singletonList(marker),
+ null,
+ NamingLens.getIdentityLens(),
+ null,
+ null)
.write(executor);
options.printWarnings();
} catch (ExecutionException e) {
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index 2e121c3..06f6d03 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
@@ -16,6 +17,7 @@
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -86,9 +88,10 @@
AppInfo appInfo = new AppInfo(app);
app = D8.optimize(app, appInfo, options, timing, executor);
+ List<Marker> markers = app.dexItemFactory.extractMarkers();
+
assert !options.hasMethodsFilter();
- new ApplicationWriter(
- app, options, D8.getMarker(options), null, NamingLens.getIdentityLens(), null, null)
+ new ApplicationWriter(app, options, markers, null, NamingLens.getIdentityLens(), null, null)
.write(executor);
options.printWarnings();
} catch (ExecutionException e) {
@@ -101,4 +104,11 @@
executor.shutdown();
}
}
+
+ public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
+ throws IOException, CompilationException {
+ InternalOptions options = command.getInternalOptions();
+ options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
+ D8.runForTesting(command.getInputApp(), options);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 8573e0f..ee6c012 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.DexIndexedConsumer.DirectoryConsumer;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexApplication.Builder;
@@ -23,6 +24,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
@@ -51,6 +53,7 @@
DexApplication app =
new ApplicationReader(command.getInputApp(), options, timing).read(null, executor);
+ List<Marker> markers = app.dexItemFactory.extractMarkers();
ClassNameMapper mapper = null;
if (proguardMap != null) {
@@ -77,7 +80,7 @@
new ApplicationWriter(
featureApp,
options,
- D8.getMarker(options),
+ markers,
null,
NamingLens.getIdentityLens(),
null,
@@ -119,4 +122,11 @@
}
return applications;
}
+
+ public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
+ throws IOException, CompilationException {
+ InternalOptions options = command.getInternalOptions();
+ options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
+ D8.runForTesting(command.getInputApp(), options);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 4f12ce1..6821c4e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -70,6 +70,7 @@
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.StandardOpenOption;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -195,7 +196,7 @@
new ApplicationWriter(
application,
options,
- marker,
+ marker == null ? null : Collections.singletonList(marker),
deadCode,
namingLens,
proguardSeedsData,
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 210a632..cde7e93 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "v1.2.9-dev";
+ public static final String LABEL = "v1.2.10-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index c2cfe42..ae43001 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -97,6 +97,11 @@
}
@Override
+ public InternalOptions getOptions() {
+ return options;
+ }
+
+ @Override
public void allocateRegisters(boolean debug) {
assert options.debug == debug;
allocateRegisters();
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 30664a2..d5a7013 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -46,7 +46,7 @@
public final NamingLens namingLens;
public final String proguardSeedsData;
public final InternalOptions options;
- public DexString markerString;
+ public List<DexString> markerStrings;
public DexIndexedConsumer programConsumer;
public final ProguardMapSupplier proguardMapSupplier;
@@ -110,7 +110,7 @@
public ApplicationWriter(
DexApplication application,
InternalOptions options,
- Marker marker,
+ List<Marker> markers,
String deadCode,
NamingLens namingLens,
String proguardSeedsData,
@@ -118,7 +118,7 @@
this(
application,
options,
- marker,
+ markers,
deadCode,
namingLens,
proguardSeedsData,
@@ -129,7 +129,7 @@
public ApplicationWriter(
DexApplication application,
InternalOptions options,
- Marker marker,
+ List<Marker> markers,
String deadCode,
NamingLens namingLens,
String proguardSeedsData,
@@ -139,9 +139,12 @@
this.application = application;
assert options != null;
this.options = options;
- this.markerString = (marker == null)
- ? null
- : application.dexItemFactory.createString(marker.toString());
+ if (markers != null && !markers.isEmpty()) {
+ this.markerStrings = new ArrayList<>();
+ for (Marker marker : markers) {
+ this.markerStrings.add(application.dexItemFactory.createString(marker.toString()));
+ }
+ }
this.deadCode = deadCode;
this.namingLens = namingLens;
this.proguardSeedsData = proguardSeedsData;
@@ -174,7 +177,9 @@
insertAttributeAnnotations();
application.dexItemFactory.sort(namingLens);
- assert this.markerString == null || application.dexItemFactory.extractMarker() != null;
+ assert this.markerStrings == null
+ || this.markerStrings.isEmpty()
+ || application.dexItemFactory.extractMarker() != null;
SortAnnotations sortAnnotations = new SortAnnotations();
application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 5369069..ae6acd9 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -275,8 +275,10 @@
mainDexFile = new VirtualFile(0, writer.namingLens);
assert virtualFiles.isEmpty();
virtualFiles.add(mainDexFile);
- if (writer.markerString != null) {
- mainDexFile.transaction.addString(writer.markerString);
+ if (writer.markerStrings != null && !writer.markerStrings.isEmpty()) {
+ for (DexString markerString : writer.markerStrings) {
+ mainDexFile.transaction.addString(markerString);
+ }
mainDexFile.commitTransaction();
}
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 5fe9134..364c0ee 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -483,7 +483,7 @@
}
// Debugging support to extract marking string.
- synchronized public Marker extractMarker() {
+ public synchronized Marker extractMarker() {
// This is slow but it is not needed for any production code yet.
for (DexString dexString : strings.keySet()) {
Marker result = Marker.parse(dexString.toString());
@@ -494,6 +494,20 @@
return null;
}
+ // Debugging support to extract marking string.
+ // Find all markers.
+ public synchronized List<Marker> extractMarkers() {
+ // This is slow but it is not needed for any production code yet.
+ List<Marker> markers = new ArrayList<>();
+ for (DexString dexString : strings.keySet()) {
+ Marker marker = Marker.parse(dexString.toString());
+ if (marker != null) {
+ markers.add(marker);
+ }
+ }
+ return markers;
+ }
+
synchronized public DexType createType(DexString descriptor) {
assert !sorted;
assert descriptor != null;
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 163c20b..60703de 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -495,6 +495,8 @@
public void removePhi(Phi phi) {
phis.remove(phi);
+ assert currentDefinitions == null || !currentDefinitions.containsValue(phi)
+ : "Attempt to remove Phi " + phi + " which is present in currentDefinitions";
}
public void add(Instruction next) {
@@ -678,7 +680,17 @@
public void replaceCurrentDefinitions(Value oldValue, Value newValue) {
assert oldValue.definition.getBlock() == this;
assert !oldValue.isUsed();
- currentDefinitions.replaceAll((index, value) -> value == oldValue ? newValue : value);
+ for (Entry<Integer, Value> entry : currentDefinitions.entrySet()) {
+ if (entry.getValue() == oldValue) {
+ if (oldValue.isPhi()) {
+ oldValue.asPhi().removeDefinitionsUser(currentDefinitions);
+ }
+ entry.setValue(newValue);
+ if (newValue.isPhi()) {
+ newValue.asPhi().addDefinitionsUser(currentDefinitions);
+ }
+ }
+ }
}
public void updateCurrentDefinition(int register, Value value, EdgeType readingEdge) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
index f751669..c2f21ac 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Binop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -26,8 +26,13 @@
public Binop(NumericType type, Value dest, Value left, Value right) {
super(dest);
this.type = type;
- addInValue(left);
- addInValue(right);
+ if (isCommutative() && (!right.isConstNumber() && left.isConstNumber())) {
+ addInValue(right);
+ addInValue(left);
+ } else {
+ addInValue(left);
+ addInValue(right);
+ }
}
public NumericType getNumericType() {
@@ -53,7 +58,8 @@
return ((leftRegister == destRegister) ||
(isCommutative() && rightRegister == destRegister)) &&
leftRegister <= U4BIT_MAX &&
- rightRegister <= U4BIT_MAX;
+ rightRegister <= U4BIT_MAX &&
+ !(allocator.getOptions().canHaveMul2AddrBug() && isMul());
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index af6d5a3..6f535bb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -103,7 +103,7 @@
}
protected int argumentRegisterValue(int i, DexBuilder builder) {
- assert needsRangedInvoke(builder);
+ assert needsRangedInvoke();
if (i < arguments().size()) {
// If argument values flow into ranged invokes, all the ranged invoke arguments
// are arguments to this method in order. Therefore, we use the incoming registers
@@ -116,9 +116,11 @@
}
protected int fillArgumentRegisters(DexBuilder builder, int[] registers) {
+ assert !needsRangedInvoke();
int i = 0;
for (Value value : arguments()) {
int register = builder.allocatedRegister(value, getNumber());
+ assert register <= Constants.U4BIT_MAX;
for (int j = 0; j < value.requiredRegisters(); j++) {
assert i < 5;
registers[i++] = register++;
@@ -127,15 +129,6 @@
return i;
}
- protected boolean hasHighArgumentRegister(DexBuilder builder) {
- for (Value value : arguments()) {
- if (builder.argumentValueUsesHighRegister(value, getNumber())) {
- return true;
- }
- }
- return false;
- }
-
protected boolean argumentsConsecutive(DexBuilder builder) {
Value value = arguments().get(0);
int next = builder.argumentOrAllocateRegister(value, getNumber()) + value.requiredRegisters();
@@ -206,19 +199,8 @@
return true;
}
- private boolean argumentsAreConsecutiveInputArgumentsWithHighRegisters(
- DexBuilder builder) {
- if (!argumentsAreConsecutiveInputArguments()) {
- return false;
- }
- Value lastArgument = arguments().get(arguments().size() - 1);
- return builder.argumentOrAllocateRegister(lastArgument, getNumber()) > Constants.U4BIT_MAX;
- }
-
- protected boolean needsRangedInvoke(DexBuilder builder) {
- return requiredArgumentRegisters() > 5
- || hasHighArgumentRegister(builder)
- || argumentsAreConsecutiveInputArgumentsWithHighRegisters(builder);
+ protected boolean needsRangedInvoke() {
+ return requiredArgumentRegisters() > 5 || argumentsAreConsecutiveInputArguments();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 0579362..9d95514 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -54,7 +54,7 @@
com.android.tools.r8.code.Instruction instruction;
int argumentRegisters = requiredArgumentRegisters();
builder.requestOutgoingRegisters(argumentRegisters);
- if (needsRangedInvoke(builder)) {
+ if (needsRangedInvoke()) {
assert argumentsConsecutive(builder);
int firstRegister = argumentRegisterValue(0, builder);
instruction = new InvokeCustomRange(firstRegister, argumentRegisters, getCallSite());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index fa85845..12c991d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -52,7 +52,7 @@
com.android.tools.r8.code.Instruction instruction;
int argumentRegisters = requiredArgumentRegisters();
builder.requestOutgoingRegisters(argumentRegisters);
- if (needsRangedInvoke(builder)) {
+ if (needsRangedInvoke()) {
assert argumentsConsecutive(builder);
int firstRegister = argumentRegisterValue(0, builder);
instruction = new InvokeDirectRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index d29e3eb..e886f27 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -46,7 +46,7 @@
com.android.tools.r8.code.Instruction instruction;
int argumentRegisters = requiredArgumentRegisters();
builder.requestOutgoingRegisters(argumentRegisters);
- if (needsRangedInvoke(builder)) {
+ if (needsRangedInvoke()) {
assert argumentsConsecutive(builder);
int firstRegister = argumentRegisterValue(0, builder);
instruction = new InvokeInterfaceRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index 41e4ccf..746f87a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -56,7 +56,7 @@
com.android.tools.r8.code.Instruction instruction;
int argumentRegisters = requiredArgumentRegisters();
builder.requestOutgoingRegisters(argumentRegisters);
- if (needsRangedInvoke(builder)) {
+ if (needsRangedInvoke()) {
assert argumentsConsecutive(builder);
int firstRegister = argumentRegisterValue(0, builder);
instruction = new FilledNewArrayRange(firstRegister, argumentRegisters, type);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 0e63cc7..624c534 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -60,7 +60,7 @@
com.android.tools.r8.code.Instruction instruction;
int argumentRegisters = requiredArgumentRegisters();
builder.requestOutgoingRegisters(argumentRegisters);
- if (needsRangedInvoke(builder)) {
+ if (needsRangedInvoke()) {
assert argumentsConsecutive(builder);
int firstRegister = argumentRegisterValue(0, builder);
instruction = new InvokePolymorphicRange(
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 896fca9..1930948 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -53,7 +53,7 @@
com.android.tools.r8.code.Instruction instruction;
int argumentRegisters = requiredArgumentRegisters();
builder.requestOutgoingRegisters(argumentRegisters);
- if (needsRangedInvoke(builder)) {
+ if (needsRangedInvoke()) {
assert argumentsConsecutive(builder);
int firstRegister = argumentRegisterValue(0, builder);
instruction = new InvokeStaticRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index c761184..3a70bac 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -58,7 +58,7 @@
com.android.tools.r8.code.Instruction instruction;
int argumentRegisters = requiredArgumentRegisters();
builder.requestOutgoingRegisters(argumentRegisters);
- if (needsRangedInvoke(builder)) {
+ if (needsRangedInvoke()) {
assert argumentsConsecutive(builder);
int firstRegister = argumentRegisterValue(0, builder);
instruction = new InvokeSuperRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 5ef74b8..50b83c6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -46,7 +46,7 @@
com.android.tools.r8.code.Instruction instruction;
int argumentRegisters = requiredArgumentRegisters();
builder.requestOutgoingRegisters(argumentRegisters);
- if (needsRangedInvoke(builder)) {
+ if (needsRangedInvoke()) {
assert argumentsConsecutive(builder);
int firstRegister = argumentRegisterValue(0, builder);
instruction = new InvokeVirtualRange(firstRegister, argumentRegisters, getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index a54208d..67a4783 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1549,8 +1549,7 @@
insertAt.nextUntil(i ->
i.inValues().contains(instruction.outValue())
|| i.isJumpInstruction()
- || (hasCatchHandlers && i.instructionTypeCanThrow())
- || (options.canHaveBoundsCheckEliminationBug() && i.isArrayLength()));
+ || (hasCatchHandlers && i.instructionTypeCanThrow()));
Instruction next = insertAt.previous();
instruction.forceSetPosition(
next.isGoto() ? next.asGoto().getTarget().getPosition() : next.getPosition());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 678ddae..7eb771c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -205,7 +205,7 @@
}
return NEVER;
} else {
- /* package-private */
+ /* package-private */
return targetHolder.isSamePackage(contextHolder) ? PACKAGE : NEVER;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index b4a1865..c4e2ab6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -160,8 +160,9 @@
int validateInstanceInitializerEpilogue(
com.android.tools.r8.code.Instruction[] instructions, int index)
throws LambdaStructureError {
- if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect) ||
- instructions[index].getMethod() != kotlin.factory.objectMethods.constructor) {
+ if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect
+ || instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange)
+ || instructions[index].getMethod() != kotlin.factory.objectMethods.constructor) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
index++;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index 46c14e9..46add5f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -170,8 +170,9 @@
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
index++;
- if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect) ||
- instructions[index].getMethod() != kotlin.functional.lambdaInitializerMethod) {
+ if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect
+ || instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange)
+ || instructions[index].getMethod() != kotlin.functional.lambdaInitializerMethod) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
index++;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
index 3410703..2385f35 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
@@ -225,8 +225,9 @@
((com.android.tools.r8.code.NewInstance) instructions[0]).getType() != lambda.type) {
throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
}
- if (!(instructions[1] instanceof com.android.tools.r8.code.InvokeDirect) ||
- !isLambdaInitializerMethod(lambda, instructions[1].getMethod())) {
+ if (!(instructions[1] instanceof com.android.tools.r8.code.InvokeDirect
+ || instructions[1] instanceof com.android.tools.r8.code.InvokeDirectRange)
+ || !isLambdaInitializerMethod(lambda, instructions[1].getMethod())) {
throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
}
if (!(instructions[2] instanceof SputObject) ||
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index c6477f3..8ad7a32 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -75,7 +75,6 @@
public static final int REGISTER_CANDIDATE_NOT_FOUND = -1;
public static final int MIN_CONSTANT_FREE_FOR_POSITIONS = 5;
- private static final int RESERVED_MOVE_EXCEPTION_REGISTER = 0;
private enum ArgumentReuseMode {
ALLOW_ARGUMENT_REUSE,
@@ -113,8 +112,6 @@
// The max register number that will fit in any dex instruction encoding.
private static final int MAX_SMALL_REGISTER = Constants.U4BIT_MAX;
- // We put one sentinel in front of the argument chain and one after the argument chain.
- private static final int NUMBER_OF_SENTINEL_REGISTERS = 2;
// The code for which to allocate registers.
private final IRCode code;
@@ -125,8 +122,8 @@
// Mapping from basic blocks to the set of values live at entry to that basic block.
private Map<BasicBlock, Set<Value>> liveAtEntrySets;
- // The sentinel value starting the chain of linked argument values.
- private Value preArgumentSentinelValue = null;
+ // The value of the first argument, or null if the method has no arguments.
+ private Value firstArgumentValue;
// The set of registers that are free for allocation.
private TreeSet<Integer> freeRegisters = new TreeSet<>();
@@ -157,9 +154,10 @@
// register.
private boolean hasDedicatedMoveExceptionRegister = false;
+ // We allocate a dedicated move exception register right after the arguments.
private int getMoveExceptionRegister() {
- return numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS +
- RESERVED_MOVE_EXCEPTION_REGISTER;
+ assert hasDedicatedMoveExceptionRegister;
+ return numberOfArgumentRegisters;
}
private int getNextUnusedRegisterNumber() {
@@ -300,10 +298,11 @@
ranges.sort(LocalRange::compareTo);
// At each instruction compute the changes to live locals.
- boolean localsChanged = false;
LinkedList<LocalRange> openRanges = new LinkedList<>();
Iterator<LocalRange> rangeIterator = ranges.iterator();
LocalRange nextStartingRange = rangeIterator.next();
+ Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>();
+ Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>();
for (BasicBlock block : blocks) {
// Skip past all spill moves to obtain the instruction number of the actual first instruction.
@@ -349,42 +348,52 @@
instruction.clearDebugValues();
instructionIterator.remove();
}
- if (isSpillInstruction(instruction)) {
+ if (!instructionIterator.hasNext()) {
+ break;
+ }
+ Instruction nextInstruction = instructionIterator.peekNext();
+ if (isSpillInstruction(nextInstruction)) {
+ // No need to insert a DebugLocalsChange instruction before a spill instruction.
continue;
}
- int index = instruction.getNumber();
+ int index = nextInstruction.getNumber();
ListIterator<LocalRange> it = openRanges.listIterator(0);
- Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>();
- Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>();
while (it.hasNext()) {
LocalRange openRange = it.next();
- // Any local change is inserted after the instruction so end is inclusive.
- if (openRange.end <= index) {
+ // Close ranges up-to but excluding the first instruction.
+ if (!isLocalLiveAtInstruction(nextInstruction, openRange)) {
it.remove();
assert currentLocals.get(openRange.register) == openRange.local;
currentLocals.remove(openRange.register);
- localsChanged = true;
ending.put(openRange.register, openRange.local);
}
}
- while (nextStartingRange != null && nextStartingRange.start <= index) {
+ while (nextStartingRange != null && nextStartingRange.start < index) {
// If the range is live at this index open it.
- if (index < nextStartingRange.end) {
+ if (isLocalLiveAtInstruction(nextInstruction, nextStartingRange)) {
openRanges.add(nextStartingRange);
assert !currentLocals.containsKey(nextStartingRange.register);
currentLocals.put(nextStartingRange.register, nextStartingRange.local);
starting.put(nextStartingRange.register, nextStartingRange.local);
- localsChanged = true;
}
nextStartingRange = rangeIterator.hasNext() ? rangeIterator.next() : null;
}
- if (localsChanged && instruction.getBlock().exit() != instruction) {
- DebugLocalsChange change = createLocalsChange(ending, starting);
- if (change != null) {
- instructionIterator.add(change);
+ // Compute the final change in locals and insert it before nextInstruction.
+ boolean localsChanged = !ending.isEmpty() || !starting.isEmpty();
+ if (localsChanged) {
+ boolean skipChange =
+ nextInstruction == nextInstruction.getBlock().exit() && nextInstruction.isGoto();
+ if (!skipChange) {
+ DebugLocalsChange change = createLocalsChange(ending, starting);
+ if (change != null) {
+ // Insert the DebugLocalsChange instruction before nextInstruction.
+ instructionIterator.add(change);
+ }
}
+ // Create new maps for the next DebugLocalsChange instruction.
+ ending = new Int2ReferenceOpenHashMap<>();
+ starting = new Int2ReferenceOpenHashMap<>();
}
- localsChanged = false;
}
}
}
@@ -393,11 +402,6 @@
return isLocalLiveAtInstruction(instruction, range.start, range.end, range.value);
}
- public static boolean isLocalLiveAtInstruction(
- Instruction instruction, LiveRange range, Value value) {
- return isLocalLiveAtInstruction(instruction, range.start, range.end, value);
- }
-
private static boolean isLocalLiveAtInstruction(
Instruction instruction, int start, int end, Value value) {
int number = instruction.getNumber();
@@ -515,12 +519,9 @@
// Compute the set of registers that is used based on all live intervals.
Set<Integer> usedRegisters = new HashSet<>();
for (LiveIntervals intervals : liveIntervals) {
- // Argument sentinel values do not occupy any registers.
- if (!isArgumentSentinelIntervals(intervals)) {
- addRegisterIfUsed(usedRegisters, intervals);
- for (LiveIntervals childIntervals : intervals.getSplitChildren()) {
- addRegisterIfUsed(usedRegisters, childIntervals);
- }
+ addRegisterIfUsed(usedRegisters, intervals);
+ for (LiveIntervals childIntervals : intervals.getSplitChildren()) {
+ addRegisterIfUsed(usedRegisters, childIntervals);
}
}
// Additionally, we have used temporary registers for parallel move scheduling, those
@@ -540,11 +541,6 @@
unusedRegisters = computed;
}
- private boolean isArgumentSentinelIntervals(LiveIntervals intervals) {
- return intervals.isArgumentInterval() &&
- (intervals.getPreviousConsecutive() == null || intervals.getNextConsecutive() == null);
- }
-
private void addRegisterIfUsed(Set<Integer> used, LiveIntervals intervals) {
boolean unused = intervals.isSpilledAndRematerializable(this);
if (!unused) {
@@ -567,7 +563,7 @@
@Override
public int registersUsed() {
- int numberOfRegister = maxRegisterNumber + 1 - NUMBER_OF_SENTINEL_REGISTERS;
+ int numberOfRegister = maxRegisterNumber + 1;
if (unusedRegisters != null) {
return numberOfRegister - unusedRegisters[unusedRegisters.length - 1];
}
@@ -594,6 +590,11 @@
return getRegisterForValue(value, instructionNumber);
}
+ @Override
+ public InternalOptions getOptions() {
+ return options;
+ }
+
private ImmutableList<BasicBlock> computeLivenessInformation() {
ImmutableList<BasicBlock> blocks = code.numberInstructions();
liveAtEntrySets = code.computeLiveAtEntrySets();
@@ -627,7 +628,7 @@
// for all uses and thereby avoid moving argument values around.
private boolean unsplitArguments() {
boolean argumentRegisterUnsplit = false;
- Value current = preArgumentSentinelValue;
+ Value current = firstArgumentValue;
while (current != null) {
LiveIntervals intervals = current.getLiveIntervals();
assert intervals.getRegisterLimit() == Constants.U16BIT_MAX;
@@ -701,15 +702,12 @@
assert allocated != NO_REGISTER;
assert allocated >= 0;
int register;
- if (allocated < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS) {
+ if (allocated < numberOfArgumentRegisters) {
// For the |numberOfArguments| first registers map to the correct argument register.
- // Due to the sentinel register in front of the arguments, the register number returned is
- // the argument number starting from one.
- register = maxRegisterNumber - (numberOfArgumentRegisters - allocated)
- - NUMBER_OF_SENTINEL_REGISTERS;
+ register = maxRegisterNumber - (numberOfArgumentRegisters - allocated - 1);
} else {
// For everything else use the lower numbers.
- register = allocated - numberOfArgumentRegisters - NUMBER_OF_SENTINEL_REGISTERS;
+ register = allocated - numberOfArgumentRegisters;
}
return register;
}
@@ -730,8 +728,10 @@
private boolean performLinearScan(ArgumentReuseMode mode) {
unhandled.addAll(liveIntervals);
- LiveIntervals argumentInterval = preArgumentSentinelValue.getLiveIntervals();
- while (argumentInterval != null) {
+
+ Value argumentValue = firstArgumentValue;
+ while (argumentValue != null) {
+ LiveIntervals argumentInterval = argumentValue.getLiveIntervals();
assert argumentInterval.getRegister() != NO_REGISTER;
unhandled.remove(argumentInterval);
if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
@@ -758,7 +758,7 @@
}
}
}
- argumentInterval = argumentInterval.getNextConsecutive();
+ argumentValue = argumentValue.getNextConsecutive();
}
// We have to be careful when it comes to the register allocated for a move exception
@@ -783,7 +783,6 @@
unhandled.remove(intervals);
moveExceptionIntervals.add(intervals);
if (moveExceptionRegister == NO_REGISTER) {
- assert RESERVED_MOVE_EXCEPTION_REGISTER == 0;
moveExceptionRegister = getFreeConsecutiveRegisters(1);
assert moveExceptionRegister == getMoveExceptionRegister();
assert !freeRegisters.contains(moveExceptionRegister);
@@ -957,6 +956,7 @@
allocateLinkedIntervals(destIntervals);
// Restore the register allocation state.
freeRegisters = savedFreeRegisters;
+ // In case maxRegisterNumber has changed, update freeRegisters.
for (int i = savedUnusedRegisterNumber, j = getNextUnusedRegisterNumber(); i < j; i++) {
freeRegisters.add(i);
}
@@ -993,7 +993,6 @@
// Exclude move exception register if the first interval overlaps a move exception interval.
if (overlapsMoveExceptionInterval(current) &&
freeRegisters.remove(getMoveExceptionRegister())) {
- assert RESERVED_MOVE_EXCEPTION_REGISTER == 0;
excludedRegisters.add(getMoveExceptionRegister());
}
// Select registers.
@@ -1260,13 +1259,9 @@
}
if (registerConstraint < Constants.U16BIT_MAX) {
- // We always have argument sentinels that will not actually occupy registers. Therefore, we
- // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit.
- registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
// We know that none of the argument registers will be reused. Therefore, we allow the
- // use of number of arguments more registers. (We will never use number of arguments +
- // number of sentinel registers of them.)
+ // use of number of arguments more registers.
registerConstraint += numberOfArgumentRegisters;
}
}
@@ -1275,26 +1270,18 @@
RegisterPositions freePositions = new RegisterPositions(registerConstraint + 1);
if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
- // The sentinel registers cannot be used and we block them.
- freePositions.set(0, 0);
if (options.debug && !code.method.accessFlags.isStatic()) {
// If we are generating debug information, we pin the this value register since the
// debugger expects to always be able to find it in the input register.
assert numberOfArgumentRegisters > 0;
- assert preArgumentSentinelValue.getNextConsecutive().requiredRegisters() == 1;
- freePositions.set(1, 0);
- }
- int lastSentinelRegister = numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
- if (lastSentinelRegister <= registerConstraint) {
- freePositions.set(lastSentinelRegister, 0);
+ assert firstArgumentValue != null && firstArgumentValue.requiredRegisters() == 1;
+ freePositions.set(0, 0);
}
} else {
// Argument reuse is not allowed and we block all the argument registers so that
// arguments are never free.
- for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
- if (i <= registerConstraint) {
- freePositions.set(i, 0);
- }
+ for (int i = 0; i < numberOfArgumentRegisters && i <= registerConstraint; i++) {
+ freePositions.set(i, 0);
}
}
@@ -1541,8 +1528,11 @@
active.add(unhandledInterval);
}
- private int getLargestCandidate(int registerConstraint, RegisterPositions freePositions,
- boolean needsRegisterPair, RegisterPositions.Type type) {
+ private int getLargestCandidate(
+ int registerConstraint,
+ RegisterPositions freePositions,
+ boolean needsRegisterPair,
+ RegisterPositions.Type type) {
int candidate = REGISTER_CANDIDATE_NOT_FOUND;
int largest = -1;
@@ -1552,6 +1542,10 @@
}
int freePosition = freePositions.get(i);
if (needsRegisterPair) {
+ if (i == numberOfArgumentRegisters - 1) {
+ // The last register of the method is |i|, so we cannot use the pair (|i|, |i+1|).
+ continue;
+ }
if (i >= registerConstraint) {
break;
}
@@ -1615,11 +1609,8 @@
private void allocateBlockedRegister(LiveIntervals unhandledInterval) {
int registerConstraint = unhandledInterval.getRegisterLimit();
if (registerConstraint < Constants.U16BIT_MAX) {
- // We always have argument sentinels that will not actually occupy registers. Therefore, we
- // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit. Additionally, we never
- // reuse argument registers and therefore allow the use of numberOfArgumentRegisters as well.
+ // We never reuse argument registers and therefore allow the use of numberOfArgumentRegisters.
registerConstraint += numberOfArgumentRegisters;
- registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
}
// Initialize all candidate registers to Integer.MAX_VALUE.
@@ -1657,7 +1648,7 @@
// Disallow the reuse of argument registers by always treating them as being used
// at instruction number 0.
- for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
+ for (int i = 0; i < numberOfArgumentRegisters; i++) {
usePositions.set(i, 0);
}
@@ -1949,8 +1940,7 @@
}
private void insertMoves() {
- SpillMoveSet spillMoves =
- new SpillMoveSet(this, code, numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS);
+ SpillMoveSet spillMoves = new SpillMoveSet(this, code, numberOfArgumentRegisters);
for (LiveIntervals intervals : liveIntervals) {
if (intervals.hasSplits()) {
LiveIntervals current = intervals;
@@ -2261,53 +2251,36 @@
return true;
}
- private Value createSentinelRegisterValue() {
- return createValue(ValueType.OBJECT);
- }
-
- private LiveIntervals createSentinelLiveInterval(Value sentinelValue) {
- LiveIntervals sentinelInterval = new LiveIntervals(sentinelValue);
- sentinelInterval.addRange(LiveRange.INFINITE);
- liveIntervals.add(sentinelInterval);
- return sentinelInterval;
- }
-
- private void linkArgumentValues() {
- Value current = preArgumentSentinelValue = createSentinelRegisterValue();
- for (Value argument : code.collectArguments()) {
- current.linkTo(argument);
- current = argument;
- }
- Value endSentinel = createSentinelRegisterValue();
- current.linkTo(endSentinel);
- }
-
- private void setupArgumentLiveIntervals() {
- Value current = preArgumentSentinelValue;
- LiveIntervals currentIntervals = createSentinelLiveInterval(current);
- current = current.getNextConsecutive();
+ private void createArgumentLiveIntervals(List<Value> arguments) {
int index = 0;
- while (current.getNextConsecutive() != null) {
+ for (Value argument : arguments) {
// Add a live range to this value from the beginning of the block up to the argument
// instruction to avoid dead arguments without a range. This may create an actually empty
// range like [0,0[ but that works, too.
- LiveIntervals argumentInterval = new LiveIntervals(current);
- argumentInterval.addRange(new LiveRange(0, index * INSTRUCTION_NUMBER_DELTA));
- index++;
+ LiveIntervals argumentInterval = new LiveIntervals(argument);
+ argumentInterval.addRange(new LiveRange(0, index));
liveIntervals.add(argumentInterval);
- currentIntervals.link(argumentInterval);
- currentIntervals = argumentInterval;
- current = current.getNextConsecutive();
+ index += INSTRUCTION_NUMBER_DELTA;
}
- currentIntervals.link(createSentinelLiveInterval(current));
+ }
+
+ private void linkArgumentValuesAndIntervals(List<Value> arguments) {
+ if (!arguments.isEmpty()) {
+ Value last = firstArgumentValue = arguments.get(0);
+ for (int i = 1; i < arguments.size(); ++i) {
+ Value next = arguments.get(i);
+ last.linkTo(next);
+ last.getLiveIntervals().link(next.getLiveIntervals());
+ last = next;
+ }
+ }
}
private void insertArgumentMoves() {
// Record the constraint that incoming arguments are in consecutive registers.
- // We also insert sentinel registers before and after the arguments at this
- // point.
- linkArgumentValues();
- setupArgumentLiveIntervals();
+ List<Value> arguments = code.collectArguments();
+ createArgumentLiveIntervals(arguments);
+ linkArgumentValuesAndIntervals(arguments);
for (BasicBlock block : code.blocks) {
InstructionListIterator it = block.listIterator();
while (it.hasNext()) {
@@ -2336,30 +2309,32 @@
private void pinArgumentRegisters() {
// Special handling for arguments. Pin their register.
- int register = 0;
- Value current = preArgumentSentinelValue;
- while (current != null) {
- LiveIntervals argumentLiveInterval = current.getLiveIntervals();
- // Pin the argument register. We use getFreeConsecutiveRegisters to make sure that we update
- // the max register number.
- register = getFreeConsecutiveRegisters(argumentLiveInterval.requiredRegisters());
- assignRegister(argumentLiveInterval, register);
- current = current.getNextConsecutive();
+ if (firstArgumentValue != null) {
+ int register = 0;
+ Value current = firstArgumentValue;
+ while (current != null) {
+ LiveIntervals argumentLiveInterval = current.getLiveIntervals();
+ // Pin the argument register. We use getFreeConsecutiveRegisters to make sure that we update
+ // the max register number.
+ register = getFreeConsecutiveRegisters(argumentLiveInterval.requiredRegisters());
+ assignRegister(argumentLiveInterval, register);
+ current = current.getNextConsecutive();
+ }
+ // If the last argument is wide, then its register will be numberOfArgumentRegisters - 2.
+ assert register == numberOfArgumentRegisters - 1 || register == numberOfArgumentRegisters - 2;
}
- assert register == numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
}
- private int getFreeConsecutiveRegisters(int numberOfRegister) {
- List<Integer> unused = new ArrayList<>();
- int first = getNextFreeRegister();
+ private int getFreeConsecutiveRegisters(int numberOfRegisters) {
+ Iterator<Integer> freeRegistersIterator = freeRegisters.iterator();
+ int first = getNextFreeRegister(freeRegistersIterator);
int current = first;
- while ((current - first + 1) != numberOfRegister) {
- for (int i = 0; i < numberOfRegister - 1; i++) {
- int next = getNextFreeRegister();
- if (next != current + 1) {
- for (int j = first; j <= current; j++) {
- unused.add(j);
- }
+ while (current - first + 1 != numberOfRegisters) {
+ for (int i = 0; i < numberOfRegisters - 1; i++) {
+ int next = getNextFreeRegister(freeRegistersIterator);
+ // We cannot allow that some are argument registers and some or not, because they will no
+ // longer be consecutive if we later decide to increment maxRegisterNumber.
+ if (next != current + 1 || next == numberOfArgumentRegisters) {
first = next;
current = first;
break;
@@ -2367,13 +2342,21 @@
current++;
}
}
- freeRegisters.addAll(unused);
+ for (int register = first; register < first + numberOfRegisters; ++register) {
+ freeRegisters.remove(register);
+ }
+ // Either all the consecutive registers are from the argument registers, or all are from the
+ // non-argument registers.
+ assert (first < numberOfArgumentRegisters
+ && first + numberOfRegisters - 1 < numberOfArgumentRegisters)
+ || (first >= numberOfArgumentRegisters
+ && first + numberOfRegisters - 1 >= numberOfArgumentRegisters);
return first;
}
- private int getNextFreeRegister() {
- if (freeRegisters.size() > 0) {
- return freeRegisters.pollFirst();
+ private int getNextFreeRegister(Iterator<Integer> freeRegistersIterator) {
+ if (freeRegistersIterator.hasNext()) {
+ return freeRegistersIterator.next();
}
maxRegisterNumber = getNextUnusedRegisterNumber();
return maxRegisterNumber;
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
index 79ba8db..8fcc6b0 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.regalloc;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.InternalOptions;
public interface RegisterAllocator {
void allocateRegisters(boolean debug);
@@ -11,4 +12,5 @@
int getRegisterForValue(Value value, int instructionNumber);
boolean argumentValueUsesHighRegister(Value value, int instructionNumber);
int getArgumentOrAllocateRegisterForValue(Value value, int instructionNumber);
+ InternalOptions getOptions();
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
index 2dbc015..3dc151d 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
@@ -296,8 +296,18 @@
scheduler.addMove(
new RegisterMove(move.to.getRegister(), move.type, move.from.getValue().definition));
} else if (move.to.getRegister() != move.from.getRegister()) {
- scheduler.addMove(
- new RegisterMove(move.to.getRegister(), move.from.getRegister(), move.type));
+ // In case the runtime might have a bound-check elimination bug we make sure to define all
+ // indexing constants with an actual const instruction rather than a move. This appears to
+ // avoid a bug where the index variable could end up being uninitialized.
+ if (code.options.canHaveBoundsCheckEliminationBug()
+ && move.from.getValue().isConstNumber()
+ && move.type == MoveType.SINGLE) {
+ scheduler.addMove(
+ new RegisterMove(move.to.getRegister(), move.type, move.from.getValue().definition));
+ } else {
+ scheduler.addMove(
+ new RegisterMove(move.to.getRegister(), move.from.getRegister(), move.type));
+ }
}
}
scheduler.schedule();
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 3372a20..de171a3 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -11,9 +11,8 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
-import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
-import java.util.Map;
+import com.google.common.collect.Sets;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -125,7 +124,7 @@
// Rebind to the lowest library class or program class.
if (target != null && target.method != method) {
DexClass targetClass = appInfo.definitionFor(target.method.holder);
- // If the target class is not public but the targeted method is, we might run into
+ // If the targetclass is not public but the targeted method is, we might run into
// visibility problems when rebinding.
if (!targetClass.accessFlags.isPublic() && target.accessFlags.isPublic()) {
// If the original class is public and this method is public, it might have been called
@@ -179,37 +178,27 @@
return null;
}
- private void computeFieldRebinding(Map<DexField, Set<DexEncodedMethod>> fields,
+ private void computeFieldRebinding(Set<DexField> fields,
BiFunction<DexType, DexField, DexEncodedField> lookup,
BiFunction<DexClass, DexField, DexEncodedField> lookupTargetOnClass) {
- for (Map.Entry<DexField, Set<DexEncodedMethod>> entry : fields.entrySet()) {
- DexField field = entry.getKey();
+ for (DexField field : fields) {
field = lense.lookupField(field, null);
DexEncodedField target = lookup.apply(field.getHolder(), field);
// Rebind to the lowest library class or program class. Do not rebind accesses to fields that
- // are not visible from the access context.
- Set<DexEncodedMethod> contexts = entry.getValue();
- if (target != null && target.field != field && contexts.stream().allMatch(context ->
- isVisibleFromOriginalContext(context.method.getHolder(), target))) {
+ // are not public, as this might lead to access violation errors.
+ if (target != null && target.field != field && isVisibleFromOtherClasses(target)) {
builder.map(field, validTargetFor(target.field, field, lookupTargetOnClass));
}
}
}
- private boolean isVisibleFromOriginalContext(DexType context, DexEncodedField field) {
- DexType holderType = field.field.getHolder();
- DexClass holder = appInfo.definitionFor(holderType);
- if (holder == null) {
- return false;
+ private boolean isVisibleFromOtherClasses(DexEncodedField field) {
+ // If the field is not public, the visibility on the class can not be a further constraint.
+ if (!field.accessFlags.isPublic()) {
+ return true;
}
- Constraint classVisibility =
- Constraint.deriveConstraint(context, holderType, holder.accessFlags, appInfo);
- if (classVisibility == Constraint.NEVER) {
- return false;
- }
- Constraint fieldVisibility =
- Constraint.deriveConstraint(context, holderType, field.accessFlags, appInfo);
- return fieldVisibility != Constraint.NEVER;
+ // If the field is public, then a non-public holder class will further constrain visibility.
+ return appInfo.definitionFor(field.field.getHolder()).accessFlags.isPublic();
}
public GraphLense run() {
@@ -224,15 +213,10 @@
// Likewise static invokes.
computeMethodRebinding(appInfo.staticInvokes, this::anyLookup);
- computeFieldRebinding(appInfo.staticFieldReads,
+ computeFieldRebinding(Sets.union(appInfo.staticFieldReads, appInfo.staticFieldWrites),
appInfo::resolveFieldOn, DexClass::lookupField);
- computeFieldRebinding(appInfo.staticFieldWrites,
+ computeFieldRebinding(Sets.union(appInfo.instanceFieldReads, appInfo.instanceFieldWrites),
appInfo::resolveFieldOn, DexClass::lookupField);
- computeFieldRebinding(appInfo.instanceFieldReads,
- appInfo::resolveFieldOn, DexClass::lookupField);
- computeFieldRebinding(appInfo.instanceFieldWrites,
- appInfo::resolveFieldOn, DexClass::lookupField);
-
return builder.build(appInfo.dexItemFactory, lense);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index cd04989..3a31203 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -102,14 +102,10 @@
Maps.newIdentityHashMap();
private final Map<DexType, Set<DexMethod>> directInvokes = Maps.newIdentityHashMap();
private final Map<DexType, Set<DexMethod>> staticInvokes = Maps.newIdentityHashMap();
- private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsWritten =
- Maps.newIdentityHashMap();
- private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsRead =
- Maps.newIdentityHashMap();
- private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsRead =
- Maps.newIdentityHashMap();
- private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsWritten =
- Maps.newIdentityHashMap();
+ private final Map<DexType, Set<DexField>> instanceFieldsWritten = Maps.newIdentityHashMap();
+ private final Map<DexType, Set<DexField>> instanceFieldsRead = Maps.newIdentityHashMap();
+ private final Map<DexType, Set<DexField>> staticFieldsRead = Maps.newIdentityHashMap();
+ private final Map<DexType, Set<DexField>> staticFieldsWritten = Maps.newIdentityHashMap();
private final ProtoLiteExtension protoLiteExtension;
private final Set<DexField> protoLiteFields = Sets.newIdentityHashSet();
@@ -368,7 +364,7 @@
@Override
public boolean registerInstanceFieldWrite(DexField field) {
- if (!registerItemWithTargetAndContext(instanceFieldsWritten, field, currentMethod)) {
+ if (!registerItemWithTarget(instanceFieldsWritten, field)) {
return false;
}
if (Log.ENABLED) {
@@ -381,7 +377,7 @@
@Override
public boolean registerInstanceFieldRead(DexField field) {
- if (!registerItemWithTargetAndContext(instanceFieldsRead, field, currentMethod)) {
+ if (!registerItemWithTarget(instanceFieldsRead, field)) {
return false;
}
if (Log.ENABLED) {
@@ -399,7 +395,7 @@
@Override
public boolean registerStaticFieldRead(DexField field) {
- if (!registerItemWithTargetAndContext(staticFieldsRead, field, currentMethod)) {
+ if (!registerItemWithTarget(staticFieldsRead, field)) {
return false;
}
if (Log.ENABLED) {
@@ -411,7 +407,7 @@
@Override
public boolean registerStaticFieldWrite(DexField field) {
- if (!registerItemWithTargetAndContext(staticFieldsWritten, field, currentMethod)) {
+ if (!registerItemWithTarget(staticFieldsWritten, field)) {
return false;
}
if (Log.ENABLED) {
@@ -1239,41 +1235,34 @@
}
}
- private Map<DexField, Set<DexEncodedMethod>> collectFields(
- Map<DexType, Set<TargetWithContext<DexField>>> map) {
- Map<DexField, Set<DexEncodedMethod>> result = new IdentityHashMap<>();
- for (Map.Entry<DexType, Set<TargetWithContext<DexField>>> entry : map.entrySet()) {
- for (TargetWithContext<DexField> fieldWithContext : entry.getValue()) {
- DexField field = fieldWithContext.getTarget();
- DexEncodedMethod context = fieldWithContext.getContext();
- result.computeIfAbsent(field, k -> Sets.newIdentityHashSet())
- .add(context);
- }
- }
- return result;
+ private Set<DexField> collectFields(Map<DexType, Set<DexField>> map) {
+ return map.values().stream().flatMap(Collection::stream)
+ .collect(Collectors.toCollection(Sets::newIdentityHashSet));
}
- Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsRead() {
- return Collections.unmodifiableMap(collectFields(instanceFieldsRead));
+ SortedSet<DexField> collectInstanceFieldsRead() {
+ return ImmutableSortedSet.copyOf(
+ PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsRead));
}
- Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsWritten() {
- return Collections.unmodifiableMap(collectFields(instanceFieldsWritten));
+ SortedSet<DexField> collectInstanceFieldsWritten() {
+ return ImmutableSortedSet.copyOf(
+ PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsWritten));
}
- Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsRead() {
- return Collections.unmodifiableMap(collectFields(staticFieldsRead));
+ SortedSet<DexField> collectStaticFieldsRead() {
+ return ImmutableSortedSet.copyOf(
+ PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsRead));
}
- Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsWritten() {
- return Collections.unmodifiableMap(collectFields(staticFieldsWritten));
+ SortedSet<DexField> collectStaticFieldsWritten() {
+ return ImmutableSortedSet.copyOf(
+ PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsWritten));
}
- private Set<DexField> collectReachedFields(
- Set<DexField> set, Function<DexField, DexField> lookup) {
- return set.stream()
- .map(lookup)
- .filter(Objects::nonNull)
+ private Set<DexField> collectReachedFields(Map<DexType, Set<DexField>> map,
+ Function<DexField, DexField> lookup) {
+ return map.values().stream().flatMap(set -> set.stream().map(lookup).filter(Objects::nonNull))
.collect(Collectors.toCollection(Sets::newIdentityHashSet));
}
@@ -1287,11 +1276,16 @@
return target == null ? null : target.field;
}
- SortedSet<DexField> mergeFieldAccesses(Set<DexField> instanceFields, Set<DexField> staticFields) {
+ SortedSet<DexField> collectFieldsRead() {
return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
- Sets.union(
- collectReachedFields(instanceFields, this::tryLookupInstanceField),
- collectReachedFields(staticFields, this::tryLookupStaticField)));
+ Sets.union(collectReachedFields(instanceFieldsRead, this::tryLookupInstanceField),
+ collectReachedFields(staticFieldsRead, this::tryLookupStaticField)));
+ }
+
+ SortedSet<DexField> collectFieldsWritten() {
+ return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
+ Sets.union(collectReachedFields(instanceFieldsWritten, this::tryLookupInstanceField),
+ collectReachedFields(staticFieldsWritten, this::tryLookupStaticField)));
}
private void markClassAsInstantiatedWithCompatRule(DexClass clazz) {
@@ -1462,21 +1456,21 @@
*/
public final SortedSet<DexField> fieldsWritten;
/**
- * Set of all field ids used in instance field reads, along with access context.
+ * Set of all field ids used in instance field reads.
*/
- public final Map<DexField, Set<DexEncodedMethod>> instanceFieldReads;
+ public final SortedSet<DexField> instanceFieldReads;
/**
- * Set of all field ids used in instance field writes, along with access context.
+ * Set of all field ids used in instance field writes.
*/
- public final Map<DexField, Set<DexEncodedMethod>> instanceFieldWrites;
+ public final SortedSet<DexField> instanceFieldWrites;
/**
- * Set of all field ids used in static static field reads, along with access context.
+ * Set of all field ids used in static static field reads.
*/
- public final Map<DexField, Set<DexEncodedMethod>> staticFieldReads;
+ public final SortedSet<DexField> staticFieldReads;
/**
- * Set of all field ids used in static field writes, along with access context.
+ * Set of all field ids used in static field writes.
*/
- public final Map<DexField, Set<DexEncodedMethod>> staticFieldWrites;
+ public final SortedSet<DexField> staticFieldWrites;
/**
* Set of all methods referenced in virtual invokes;
*/
@@ -1548,10 +1542,8 @@
this.instanceFieldWrites = enqueuer.collectInstanceFieldsWritten();
this.staticFieldReads = enqueuer.collectStaticFieldsRead();
this.staticFieldWrites = enqueuer.collectStaticFieldsWritten();
- this.fieldsRead = enqueuer.mergeFieldAccesses(
- instanceFieldReads.keySet(), staticFieldReads.keySet());
- this.fieldsWritten = enqueuer.mergeFieldAccesses(
- instanceFieldWrites.keySet(), staticFieldWrites.keySet());
+ this.fieldsRead = enqueuer.collectFieldsRead();
+ this.fieldsWritten = enqueuer.collectFieldsWritten();
this.pinnedItems = rewritePinnedItemsToDescriptors(enqueuer.pinnedItems);
this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
this.interfaceInvokes = joinInvokedMethods(enqueuer.interfaceInvokes);
@@ -1567,8 +1559,8 @@
this.prunedTypes = Collections.emptySet();
this.switchMaps = Collections.emptyMap();
this.ordinalsMaps = Collections.emptyMap();
- assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
- assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
+ assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
+ assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
}
private AppInfoWithLiveness(AppInfoWithLiveness previous, DexApplication application,
@@ -1601,8 +1593,8 @@
this.prunedTypes = mergeSets(previous.prunedTypes, removedClasses);
this.switchMaps = previous.switchMaps;
this.ordinalsMaps = previous.ordinalsMaps;
- assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
- assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
+ assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
+ assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
}
private AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1614,14 +1606,10 @@
this.targetedMethods = rewriteItems(previous.targetedMethods, lense::lookupMethod);
this.liveMethods = rewriteItems(previous.liveMethods, lense::lookupMethod);
this.liveFields = rewriteItems(previous.liveFields, lense::lookupField);
- this.instanceFieldReads =
- rewriteKeysWhileMergingValues(previous.instanceFieldReads, lense::lookupField);
- this.instanceFieldWrites =
- rewriteKeysWhileMergingValues(previous.instanceFieldWrites, lense::lookupField);
- this.staticFieldReads =
- rewriteKeysWhileMergingValues(previous.staticFieldReads, lense::lookupField);
- this.staticFieldWrites =
- rewriteKeysWhileMergingValues(previous.staticFieldWrites, lense::lookupField);
+ this.instanceFieldReads = rewriteItems(previous.instanceFieldReads, lense::lookupField);
+ this.instanceFieldWrites = rewriteItems(previous.instanceFieldWrites, lense::lookupField);
+ this.staticFieldReads = rewriteItems(previous.staticFieldReads, lense::lookupField);
+ this.staticFieldWrites = rewriteItems(previous.staticFieldWrites, lense::lookupField);
this.fieldsRead = rewriteItems(previous.fieldsRead, lense::lookupField);
this.fieldsWritten = rewriteItems(previous.fieldsWritten, lense::lookupField);
this.pinnedItems = rewriteMixedItems(previous.pinnedItems, lense);
@@ -1647,8 +1635,8 @@
this.ordinalsMaps = rewriteKeys(previous.ordinalsMaps, lense::lookupType);
this.protoLiteFields = previous.protoLiteFields;
// Sanity check sets after rewriting.
- assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
- assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
+ assert Sets.intersection(instanceFieldReads, staticFieldReads).isEmpty();
+ assert Sets.intersection(instanceFieldWrites, staticFieldWrites).isEmpty();
}
public AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1783,18 +1771,6 @@
return builder.build();
}
- private static <T extends PresortedComparable<T>, S> Map<T, Set<S>>
- rewriteKeysWhileMergingValues(
- Map<T, Set<S>> original, BiFunction<T, DexEncodedMethod, T> rewrite) {
- Map<T, Set<S>> result = new IdentityHashMap<>();
- for (T item : original.keySet()) {
- T rewrittenKey = rewrite.apply(item, null);
- result.computeIfAbsent(rewrittenKey, k -> Sets.newIdentityHashSet())
- .addAll(original.get(item));
- }
- return Collections.unmodifiableMap(result);
- }
-
private static ImmutableSet<DexItem> rewriteMixedItems(
Set<DexItem> original, GraphLense lense) {
ImmutableSet.Builder<DexItem> builder = ImmutableSet.builder();
@@ -2146,10 +2122,6 @@
return target;
}
- public DexEncodedMethod getContext() {
- return context;
- }
-
@Override
public int hashCode() {
return target.hashCode() * 31 + context.hashCode();
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 04c5f72..4099912 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -411,6 +411,7 @@
public boolean invertConditionals = false;
public boolean placeExceptionalBlocksLast = false;
+ public boolean dontCreateMarkerInD8 = false;
}
public boolean canUseInvokePolymorphicOnVarHandle() {
@@ -487,18 +488,16 @@
// Some Lollipop versions of Art found in the wild perform invalid bounds
// check elimination. There is a fast path of loops and a slow path.
// The bailout to the slow path is performed too early and therefore
- // the loop variable might not be defined in the slow path code leading
+ // the array-index variable might not be defined in the slow path code leading
// to use of undefined registers as indices into arrays. The result
// is ArrayIndexOutOfBounds exceptions.
//
- // In an attempt to help these Art VMs get the loop variable initialized
- // early, we do not lower constants past array-length instructions when
- // building for Lollipop or below.
+ // In an attempt to help these Art VMs, all single-width constants are initialized and not moved.
//
// There is no guarantee that this works, but it does make the problem
// disappear on the one known instance of this problem.
//
- // See b/69364976.
+ // See b/69364976 and b/77996377.
public boolean canHaveBoundsCheckEliminationBug() {
return minApiLevel <= AndroidApiLevel.L.getLevel();
}
@@ -557,4 +556,26 @@
public boolean canHaveCmpLongBug() {
return minApiLevel < AndroidApiLevel.L.getLevel();
}
+
+ // Some Lollipop VMs incorrectly optimize code with mul2addr instructions. In particular,
+ // the following hash code method produces wrong results after optimizations:
+ //
+ // 0: 0x00: IgetObject v0, v3, Field java.lang.Class MultiClassKey.first
+ // 1: 0x02: InvokeVirtual { v0 } Ljava/lang/Object;->hashCode()I
+ // 2: 0x05: MoveResult v0
+ // 3: 0x06: Const16 v1, 0x001f (31)
+ // 4: 0x08: MulInt2Addr v1, v0
+ // 5: 0x09: IgetObject v2, v3, Field java.lang.Class MultiClassKey.second
+ // 6: 0x0b: InvokeVirtual { v2 } Ljava/lang/Object;->hashCode()I
+ // 7: 0x0e: MoveResult v2
+ // 8: 0x0f: AddInt2Addr v1, v2
+ // 9: 0x10: Return v1
+ //
+ // It seems that the issue is the MulInt2Addr instructions. Avoiding that, the VM computes
+ // hash codes correctly also after optimizations.
+ //
+ // This issue has only been observed on a Verizon Ellipsis 8 tablet. See b/76115465.
+ public boolean canHaveMul2AddrBug() {
+ return minApiLevel < AndroidApiLevel.M.getLevel();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 923398d..4d7240e 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -550,6 +550,24 @@
// Tests where the output of R8 fails when run with Art.
private static final Multimap<String, TestCondition> failingRunWithArt =
new ImmutableListMultimap.Builder<String, TestCondition>()
+ // This test relies on specific field access patterns, which we rewrite.
+ .put("064-field-access",
+ TestCondition.match(
+ TestCondition.R8DEX_NOT_AFTER_D8_COMPILER,
+ TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
+ .put("064-field-access",
+ TestCondition.match(
+ TestCondition.R8DEX_COMPILER,
+ TestCondition.runtimes(
+ DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
+ DexVm.Version.V5_1_1)))
+ .put("064-field-access",
+ TestCondition.match(
+ TestCondition.tools(DexTool.NONE),
+ TestCondition.D8_AFTER_R8CF_COMPILER,
+ TestCondition.runtimes(
+ DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
+ DexVm.Version.V5_1_1)))
// The growth limit test fails after processing by R8 because R8 will eliminate an
// "unneeded" const store. The following reflective call to the VM's GC will then see the
// large array as still live and the subsequent allocations will fail to reach the desired
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
index e99e696..2b2a40b 100644
--- a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
@@ -8,12 +8,15 @@
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexFileMergerHelper;
+import com.android.tools.r8.ExtractMarker;
import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.ResourceException;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.maindexlist.MainDexListTests;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
@@ -35,16 +38,57 @@
@Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
- @Test
- public void mergeTwoFiles() throws CompilationFailedException, IOException {
+ private Path createMergerInputWithTwoClasses(OutputMode outputMode, boolean addMarker)
+ throws CompilationFailedException, CompilationException, IOException {
// Compile Class1 and Class2
Path mergerInputZip = temp.newFolder().toPath().resolve("merger-input.zip");
- D8.run(
+ D8Command command =
D8Command.builder()
- .setOutput(mergerInputZip, OutputMode.DexFilePerClassFile)
+ .setOutput(mergerInputZip, outputMode)
.addProgramFiles(Paths.get(CLASS1_CLASS))
.addProgramFiles(Paths.get(CLASS2_CLASS))
- .build());
+ .build();
+
+ DexFileMergerHelper.runD8ForTesting(command, !addMarker);
+
+ return mergerInputZip;
+ }
+
+ private void testMarker(boolean addMarkerToInput)
+ throws CompilationFailedException, CompilationException, IOException, ResourceException,
+ ExecutionException {
+ Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexIndexed, addMarkerToInput);
+
+ Marker inputMarker = ExtractMarker.extractMarkerFromDexFile(mergerInputZip);
+ assertEquals(addMarkerToInput, inputMarker != null);
+
+ Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
+ DexFileMerger.main(
+ new String[] {
+ "--input", mergerInputZip.toString(), "--output", mergerOutputZip.toString()
+ });
+
+ Marker outputMarker = ExtractMarker.extractMarkerFromDexFile(mergerOutputZip);
+ assertEquals(addMarkerToInput, outputMarker != null);
+ }
+
+ @Test
+ public void testMarkerPreserved()
+ throws CompilationFailedException, CompilationException, IOException, ResourceException,
+ ExecutionException {
+ testMarker(true);
+ }
+
+ @Test
+ public void testMarkerNotAdded()
+ throws CompilationFailedException, CompilationException, IOException, ResourceException,
+ ExecutionException {
+ testMarker(false);
+ }
+
+ @Test
+ public void mergeTwoFiles() throws CompilationFailedException, CompilationException, IOException {
+ Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexFilePerClassFile, false);
Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
DexFileMerger.main(
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index 2db8fe0..ed293f6 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -10,14 +10,16 @@
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexSplitterHelper;
+import com.android.tools.r8.ExtractMarker;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dexsplitter.DexSplitter.Options;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
@@ -53,6 +55,65 @@
@Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+ private Path createInput(boolean dontCreateMarkerInD8)
+ throws IOException, CompilationFailedException, CompilationException {
+ // Initial normal compile to create dex files.
+ Path inputZip = temp.newFolder().toPath().resolve("input.zip");
+ D8Command command =
+ D8Command.builder()
+ .setOutput(inputZip, OutputMode.DexIndexed)
+ .addProgramFiles(Paths.get(CLASS1_CLASS))
+ .addProgramFiles(Paths.get(CLASS2_CLASS))
+ .addProgramFiles(Paths.get(CLASS3_CLASS))
+ .addProgramFiles(Paths.get(CLASS3_INNER_CLASS))
+ .addProgramFiles(Paths.get(CLASS4_CLASS))
+ .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
+ .build();
+
+ DexSplitterHelper.runD8ForTesting(command, dontCreateMarkerInD8);
+
+ return inputZip;
+ }
+
+ private void testMarker(boolean addMarkerToInput)
+ throws CompilationFailedException, CompilationException, IOException, ResourceException,
+ ExecutionException {
+ Path inputZip = createInput(!addMarkerToInput);
+
+ Path output = temp.newFolder().toPath().resolve("output");
+ Files.createDirectory(output);
+ Path splitSpec = createSplitSpec();
+
+ DexSplitter.main(
+ new String[] {
+ "--input", inputZip.toString(),
+ "--output", output.toString(),
+ "--feature-splits", splitSpec.toString()
+ });
+
+ Path base = output.resolve("base").resolve("classes.dex");
+ Path feature = output.resolve("feature1").resolve("classes.dex");
+
+ for (Path path : new Path[] {inputZip, base, feature}) {
+ Marker marker = ExtractMarker.extractMarkerFromDexFile(path);
+ assertEquals(addMarkerToInput, marker != null);
+ }
+ }
+
+ @Test
+ public void testMarkerPreserved()
+ throws CompilationFailedException, CompilationException, IOException, ResourceException,
+ ExecutionException {
+ testMarker(true);
+ }
+
+ @Test
+ public void testMarkerNotAdded()
+ throws CompilationFailedException, CompilationException, IOException, ResourceException,
+ ExecutionException {
+ testMarker(false);
+ }
+
/**
* To test the file splitting we have 3 classes that we distribute like this: Class1 -> base
* Class2 -> feature1 Class3 -> feature1
@@ -71,19 +132,7 @@
private void noObfuscation(boolean useOptions)
throws IOException, CompilationFailedException, FeatureMappingException,
ResourceException, ExecutionException, CompilationException {
- // Initial normal compile to create dex files.
- Path inputZip = temp.newFolder().toPath().resolve("input.zip");
- D8.run(
- D8Command.builder()
- .setOutput(inputZip, OutputMode.DexIndexed)
- .addProgramFiles(Paths.get(CLASS1_CLASS))
- .addProgramFiles(Paths.get(CLASS2_CLASS))
- .addProgramFiles(Paths.get(CLASS3_CLASS))
- .addProgramFiles(Paths.get(CLASS3_INNER_CLASS))
- .addProgramFiles(Paths.get(CLASS4_CLASS))
- .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
- .build());
-
+ Path inputZip = createInput(false);
Path output = temp.newFolder().toPath().resolve("output");
Files.createDirectory(output);
Path splitSpec = createSplitSpec();
@@ -189,19 +238,7 @@
private void splitFromJars(boolean useOptions, boolean explicitBase)
throws IOException, CompilationFailedException, FeatureMappingException, ResourceException,
ExecutionException, CompilationException {
- // Initial normal compile to create dex files.
- Path inputZip = temp.newFolder().toPath().resolve("input.zip");
- D8.run(
- D8Command.builder()
- .setOutput(inputZip, OutputMode.DexIndexed)
- .addProgramFiles(Paths.get(CLASS1_CLASS))
- .addProgramFiles(Paths.get(CLASS2_CLASS))
- .addProgramFiles(Paths.get(CLASS3_CLASS))
- .addProgramFiles(Paths.get(CLASS3_INNER_CLASS))
- .addProgramFiles(Paths.get(CLASS4_CLASS))
- .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
- .build());
-
+ Path inputZip = createInput(false);
Path output = temp.newFolder().toPath().resolve("output");
Files.createDirectory(output);
Path baseJar = temp.getRoot().toPath().resolve("base.jar");
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTest.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTest.java
new file mode 100644
index 0000000..98db705
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2018, 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;
+
+public class PhiDefinitionsTest {
+
+ public static Class[] CLASSES = {
+ PhiDefinitionsTest.class,
+ MethodWriter.class,
+ };
+
+ static class MethodWriter {
+ public int exceptionCount;
+ }
+
+ public static void main(String[] args) {
+ if (args.length >= 42) {
+ System.out.println(new PhiDefinitionsTest().readMethod(args.length));
+ }
+ }
+
+ private int readMethod(int u) {
+ u += 6;
+ int exception = 0;
+ for (int i = count(u); i > 0; --i) {
+ exception = count(u);
+ for (int j = count(); j > 0; --j) {
+ read(exception);
+ exception += 2;
+ }
+ u += 6 + count(u + 4);
+ }
+ u += 2;
+ MethodWriter mv = visitMethod();
+ if (cond() && cond() && cond()) {
+ MethodWriter mw = mv;
+ boolean sameExceptions = false;
+ if (count() == mw.exceptionCount) {
+ sameExceptions = true;
+ for (int j = count(); j >= 0; --j) {
+ exception -= 2;
+ }
+ }
+ if (cond()) {
+ return u;
+ }
+ }
+ return u;
+ }
+
+ private native MethodWriter visitMethod();
+
+ private native boolean cond();
+
+ private native String read(int i);
+
+ private native int count(int arg);
+
+ private native int count();
+}
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestDump.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestDump.java
new file mode 100644
index 0000000..5a93117
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestDump.java
@@ -0,0 +1,509 @@
+// Copyright (c) 2018, 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;
+
+import org.objectweb.asm.*;
+
+public class PhiDefinitionsTestDump implements Opcodes {
+
+ private static final String SIMPLE_NAME = "PhiDefinitionsTest";
+ static final String INTERNAL_NAME = "com/android/tools/r8/ir/" + SIMPLE_NAME;
+ private static final String INNER_SIMPLE_NAME = "MethodWriter";
+ static final String INNER_INTERNAL_NAME = INTERNAL_NAME + "$" + INNER_SIMPLE_NAME;
+
+ // static String INTERNAL_NAME = "com/android/tools/r8/ir/PhiDefinitionsTest";
+ // private static String INNER_SIMPLE_NAME = "MethodWriter";
+ // static String INNER_INTERNAL_NAME = INTERNAL_NAME + '$' + INNER_SIMPLE_NAME;
+
+ public static byte[] dump() throws Exception {
+
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ AnnotationVisitor av0;
+
+ cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, INTERNAL_NAME, null, "java/lang/Object", null);
+
+ cw.visitSource(SIMPLE_NAME + ".java", null);
+
+ cw.visitInnerClass(INNER_INTERNAL_NAME, INTERNAL_NAME, INNER_SIMPLE_NAME, ACC_STATIC);
+
+ method0(cw);
+ method1(cw);
+ method2(cw);
+ nativeMethod(cw, "visitMethod", "()L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";");
+ nativeMethod(cw, "cond", "()Z");
+ nativeMethod(cw, "read", "(I)Ljava/lang/String;");
+ nativeMethod(cw, "count", "(I)I");
+ nativeMethod(cw, "count", "()I");
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ private static void nativeMethod(ClassWriter cw, String name, String desc) {
+ MethodVisitor mv;
+ mv = cw.visitMethod(ACC_PRIVATE + ACC_NATIVE, name, desc, null, null);
+ mv.visitEnd();
+ }
+
+ private static void method0(ClassWriter cw) {
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ Label[] labels = new Label[2];
+ for (int i = 0; i < labels.length; i++) {
+ labels[i] = new Label();
+ }
+ mv.visitCode();
+ mv.visitLabel(labels[0]);
+ mv.visitLineNumber(6, labels[0]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitLabel(labels[1]);
+ mv.visitLocalVariable("this", "L" + INTERNAL_NAME + ";", null, labels[0], labels[1], 0);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+
+ private static void method1(ClassWriter cw) {
+ MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ Label[] labels = new Label[4];
+ for (int i = 0; i < labels.length; i++) {
+ labels[i] = new Label();
+ }
+ mv.visitCode();
+ mv.visitLabel(labels[0]);
+ mv.visitLineNumber(18, labels[0]);
+ mv.visitLdcInsn(new Integer(42));
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitJumpInsn(IF_ICMPLT, labels[1]);
+ mv.visitLabel(labels[2]);
+ mv.visitLineNumber(19, labels[2]);
+ mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ mv.visitVarInsn(ASTORE, 1);
+ mv.visitTypeInsn(NEW, INTERNAL_NAME);
+ mv.visitVarInsn(ASTORE, 2);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "<init>", "()V", false);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "readMethod", "(I)I", false);
+ mv.visitVarInsn(ISTORE, 2);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ILOAD, 2);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
+ mv.visitLabel(labels[1]);
+ mv.visitLineNumber(21, labels[1]);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitInsn(RETURN);
+ mv.visitLabel(labels[3]);
+ mv.visitLocalVariable("args", "[Ljava/lang/String;", null, labels[0], labels[3], 0);
+ mv.visitMaxs(2, 4);
+ mv.visitEnd();
+ }
+
+ private static void method2(ClassWriter cw) {
+ MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "readMethod", "(I)I", null, null);
+ Label[] labels = new Label[31];
+ for (int i = 0; i < labels.length; i++) {
+ labels[i] = new Label();
+ }
+ mv.visitCode();
+ mv.visitLabel(labels[0]);
+ mv.visitLineNumber(24, labels[0]);
+ mv.visitLdcInsn(new Integer(6));
+ mv.visitInsn(POP);
+ mv.visitLdcInsn(new Integer(6));
+ mv.visitVarInsn(ISTORE, 2);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitVarInsn(ILOAD, 2);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitLabel(labels[1]);
+ mv.visitLineNumber(25, labels[1]);
+ mv.visitInsn(ICONST_0);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitLabel(labels[2]);
+ mv.visitLineNumber(26, labels[2]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "(I)I", false);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitLabel(labels[3]);
+ mv.visitFrame(
+ Opcodes.F_APPEND,
+ 3,
+ new Object[] {Opcodes.INTEGER, Opcodes.INTEGER, Opcodes.INTEGER},
+ 0,
+ null);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitJumpInsn(IFLE, labels[4]);
+ mv.visitLabel(labels[5]);
+ mv.visitLineNumber(27, labels[5]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "(I)I", false);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitLabel(labels[6]);
+ mv.visitLineNumber(28, labels[6]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "()I", false);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitLabel(labels[7]);
+ mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {Opcodes.INTEGER}, 0, null);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitJumpInsn(IFLE, labels[8]);
+ mv.visitLabel(labels[9]);
+ mv.visitLineNumber(29, labels[9]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "read", "(I)Ljava/lang/String;", false);
+ mv.visitInsn(POP);
+ mv.visitLabel(labels[10]);
+ mv.visitLineNumber(30, labels[10]);
+ mv.visitInsn(ICONST_2);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_2);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitLabel(labels[11]);
+ mv.visitLineNumber(28, labels[11]);
+ mv.visitInsn(ICONST_M1);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_M1);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitJumpInsn(GOTO, labels[7]);
+ mv.visitLabel(labels[8]);
+ mv.visitLineNumber(32, labels[8]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 6,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER,
+ Opcodes.TOP,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitLdcInsn(new Integer(6));
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_4);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "(I)I", false);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 2);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitLabel(labels[12]);
+ mv.visitLineNumber(26, labels[12]);
+ mv.visitInsn(ICONST_M1);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_M1);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitJumpInsn(GOTO, labels[3]);
+ mv.visitLabel(labels[4]);
+ mv.visitLineNumber(34, labels[4]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 5,
+ new Object[] {INTERNAL_NAME, Opcodes.TOP, Opcodes.TOP, Opcodes.INTEGER, Opcodes.INTEGER},
+ 0,
+ new Object[] {});
+ mv.visitInsn(ICONST_2);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_2);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 1);
+ mv.visitLabel(labels[13]);
+ mv.visitLineNumber(35, labels[13]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(
+ INVOKESPECIAL,
+ INTERNAL_NAME,
+ "visitMethod",
+ "()L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";",
+ false);
+ mv.visitVarInsn(ASTORE, 2);
+ mv.visitLabel(labels[14]);
+ mv.visitLineNumber(36, labels[14]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+ mv.visitJumpInsn(IFEQ, labels[15]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+ mv.visitJumpInsn(IFEQ, labels[15]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+ mv.visitJumpInsn(IFEQ, labels[15]);
+ mv.visitLabel(labels[16]);
+ mv.visitLineNumber(37, labels[16]);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitVarInsn(ASTORE, 3);
+ mv.visitLabel(labels[17]);
+ mv.visitLineNumber(38, labels[17]);
+ mv.visitInsn(ICONST_0);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitLabel(labels[18]);
+ mv.visitLineNumber(39, labels[18]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "()I", false);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitFieldInsn(GETFIELD, INNER_INTERNAL_NAME, "exceptionCount", "I");
+ mv.visitVarInsn(ISTORE, 7);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitVarInsn(ILOAD, 7);
+ mv.visitJumpInsn(IF_ICMPNE, labels[19]);
+ mv.visitLabel(labels[20]);
+ mv.visitLineNumber(40, labels[20]);
+ mv.visitInsn(ICONST_1);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitLabel(labels[21]);
+ mv.visitLineNumber(41, labels[21]);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "count", "()I", false);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitLabel(labels[22]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 7,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ INNER_INTERNAL_NAME,
+ INNER_INTERNAL_NAME,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitJumpInsn(IFLT, labels[23]);
+ mv.visitLabel(labels[24]);
+ mv.visitLineNumber(42, labels[24]);
+ mv.visitLdcInsn(new Integer(-2));
+ mv.visitVarInsn(ISTORE, 7);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitVarInsn(ILOAD, 7);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 6);
+ mv.visitLabel(labels[25]);
+ mv.visitLineNumber(41, labels[25]);
+ mv.visitInsn(ICONST_M1);
+ mv.visitInsn(POP);
+ mv.visitInsn(ICONST_M1);
+ mv.visitVarInsn(ISTORE, 7);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ILOAD, 7);
+ mv.visitInsn(IADD);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitJumpInsn(GOTO, labels[22]);
+ mv.visitLabel(labels[23]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 7,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ INNER_INTERNAL_NAME,
+ INNER_INTERNAL_NAME,
+ Opcodes.TOP,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ILOAD, 6);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitJumpInsn(GOTO, labels[26]);
+ mv.visitLabel(labels[19]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 6,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ INNER_INTERNAL_NAME,
+ INNER_INTERNAL_NAME,
+ Opcodes.INTEGER,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 5);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitLabel(labels[26]);
+ mv.visitLineNumber(45, labels[26]);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "cond", "()Z", false);
+ mv.visitJumpInsn(IFEQ, labels[27]);
+ mv.visitLabel(labels[28]);
+ mv.visitLineNumber(46, labels[28]);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitInsn(IRETURN);
+ mv.visitLabel(labels[27]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 6,
+ new Object[] {
+ INTERNAL_NAME,
+ Opcodes.INTEGER,
+ INNER_INTERNAL_NAME,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 5);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitJumpInsn(GOTO, labels[29]);
+ mv.visitLabel(labels[15]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 5,
+ new Object[] {
+ INTERNAL_NAME, Opcodes.INTEGER, INNER_INTERNAL_NAME, Opcodes.TOP, Opcodes.INTEGER
+ },
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitVarInsn(ISTORE, 3);
+ mv.visitLabel(labels[29]);
+ mv.visitLineNumber(49, labels[29]);
+ mv.visitFrame(
+ Opcodes.F_FULL,
+ 4,
+ new Object[] {INTERNAL_NAME, Opcodes.INTEGER, INNER_INTERNAL_NAME, Opcodes.INTEGER},
+ 0,
+ new Object[] {});
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitInsn(IRETURN);
+ mv.visitLabel(labels[30]);
+ mv.visitLocalVariable("u", "I", null, labels[0], labels[3], 1);
+ mv.visitLocalVariable("exception", "I", null, labels[2], labels[3], 3);
+ mv.visitLocalVariable("exception", "I", null, labels[3], labels[7], 4);
+ mv.visitLocalVariable("j", "I", null, labels[7], labels[8], 4);
+ mv.visitLocalVariable("i", "I", null, labels[3], labels[4], 1);
+ mv.visitLocalVariable("exception", "I", null, labels[7], labels[4], 5);
+ mv.visitLocalVariable("u", "I", null, labels[3], labels[13], 3);
+ mv.visitLocalVariable("exception", "I", null, labels[4], labels[22], 4);
+ mv.visitLocalVariable("j", "I", null, labels[22], labels[23], 4);
+ mv.visitLocalVariable("exception", "I", null, labels[22], labels[19], 6);
+ mv.visitLocalVariable("exception", "I", null, labels[19], labels[26], 4);
+ mv.visitLocalVariable("sameExceptions", "Z", null, labels[18], labels[26], 5);
+ mv.visitLocalVariable(
+ "mw", "L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";", null, labels[17], labels[27], 3);
+ mv.visitLocalVariable("sameExceptions", "Z", null, labels[26], labels[27], 4);
+ mv.visitLocalVariable("exception", "I", null, labels[26], labels[15], 5);
+ mv.visitLocalVariable("exception", "I", null, labels[15], labels[29], 4);
+ mv.visitLocalVariable("this", "L" + INTERNAL_NAME + ";", null, labels[0], labels[30], 0);
+ mv.visitLocalVariable("u", "I", null, labels[13], labels[30], 1);
+ mv.visitLocalVariable("exception", "I", null, labels[29], labels[30], 3);
+ mv.visitLocalVariable(
+ "mv", "L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";", null, labels[14], labels[30], 2);
+ mv.visitMaxs(3, 8);
+ mv.visitEnd();
+ }
+
+ public static byte[] dumpInner() throws Exception {
+
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ MethodVisitor mv;
+ AnnotationVisitor av0;
+
+ cw.visit(V1_8, ACC_SUPER, INNER_INTERNAL_NAME, null, "java/lang/Object", null);
+
+ cw.visitSource(SIMPLE_NAME + ".java", null);
+
+ cw.visitInnerClass(INNER_INTERNAL_NAME, INTERNAL_NAME, INNER_SIMPLE_NAME, ACC_STATIC);
+
+ {
+ fv = cw.visitField(ACC_PUBLIC, "exceptionCount", "I", null, null);
+ fv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(0, "<init>", "()V", null, null);
+ mv.visitCode();
+ Label l0 = new Label();
+ mv.visitLabel(l0);
+ mv.visitLineNumber(13, l0);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ Label l1 = new Label();
+ mv.visitLabel(l1);
+ mv.visitLocalVariable(
+ "this", "L" + INTERNAL_NAME + "$" + INNER_SIMPLE_NAME + ";", null, l0, l1, 0);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java
new file mode 100644
index 0000000..f9541e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2018, 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;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Test;
+
+public class PhiDefinitionsTestRunner extends TestBase {
+
+ private ProcessResult runInput;
+ private String className = PhiDefinitionsTest.class.getName();
+
+ private Path writeAndRunOriginal() throws IOException {
+ Path originalJar = temp.getRoot().toPath().resolve("originput.jar");
+ ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(originalJar);
+ for (Class clazz : PhiDefinitionsTest.CLASSES) {
+ String descriptor = DescriptorUtils.javaTypeToDescriptor(clazz.getName());
+ consumer.accept(ToolHelper.getClassAsBytes(clazz), descriptor, null);
+ }
+ consumer.finished(null);
+ runOriginalJar(originalJar);
+ return originalJar;
+ }
+
+ private void runOriginalJar(Path originalJar) throws IOException {
+ runInput = ToolHelper.runJava(originalJar, className);
+ if (runInput.exitCode != 0) {
+ System.out.println(runInput);
+ }
+ assertEquals(0, runInput.exitCode);
+ }
+
+ private Path writeAndRunInputJar() throws Exception {
+ Path originalJar = writeAndRunOriginal();
+ Path inputJar = temp.getRoot().toPath().resolve("input.jar");
+ build(originalJar, new ClassFileConsumer.ArchiveConsumer(inputJar));
+ runCf(inputJar, className);
+ return inputJar;
+ }
+
+ private Path writeAndRunDumpJar() throws Exception {
+ Path dumpJar = temp.getRoot().toPath().resolve("dump.jar");
+ ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(dumpJar);
+ String desc = 'L' + PhiDefinitionsTestDump.INTERNAL_NAME + ';';
+ consumer.accept(PhiDefinitionsTestDump.dump(), desc, null);
+ String innerDesc = 'L' + PhiDefinitionsTestDump.INNER_INTERNAL_NAME + ';';
+ consumer.accept(PhiDefinitionsTestDump.dumpInner(), innerDesc, null);
+ consumer.finished(null);
+ runOriginalJar(dumpJar);
+ return dumpJar;
+ }
+
+ @Test
+ public void testCf() throws Exception {
+ Path outCf = temp.getRoot().toPath().resolve("cf.zip");
+ build(writeAndRunInputJar(), new ClassFileConsumer.ArchiveConsumer(outCf));
+ runCf(outCf, className);
+ }
+
+ @Test
+ public void testDex() throws Exception {
+ Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+ build(writeAndRunInputJar(), new DexIndexedConsumer.ArchiveConsumer(outDex));
+ runDex(outDex, className);
+ }
+
+ @Test
+ public void testCfDump() throws Exception {
+ Path outCf = temp.getRoot().toPath().resolve("dump-cf.zip");
+ build(writeAndRunDumpJar(), new ClassFileConsumer.ArchiveConsumer(outCf));
+ runCf(outCf, className);
+ }
+
+ @Test
+ public void testDexDump() throws Exception {
+ Path outDex = temp.getRoot().toPath().resolve("dump-dex.zip");
+ build(writeAndRunDumpJar(), new DexIndexedConsumer.ArchiveConsumer(outDex));
+ runDex(outDex, className);
+ }
+
+ private void runCf(Path outCf, String className) throws Exception {
+ ProcessResult runCf = ToolHelper.runJava(outCf, className);
+ assertEquals(runInput.toString(), runCf.toString());
+ }
+
+ private void runDex(Path outDex, String className) throws Exception {
+ ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), className);
+ assertEquals(runInput.stdout, runDex.stdout);
+ assertEquals(runInput.exitCode, runDex.exitCode);
+ }
+
+ private void build(Path inputJar, ProgramConsumer consumer) throws Exception {
+ Builder builder =
+ R8Command.builder()
+ .setMode(CompilationMode.DEBUG)
+ .setProgramConsumer(consumer)
+ .addProgramFiles(inputJar);
+ if (consumer instanceof ClassFileConsumer) {
+ builder.addLibraryFiles(Paths.get(ToolHelper.JAVA_8_RUNTIME));
+ } else {
+ builder.addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()));
+ }
+ // TODO(b/75997473): Enable inlining when supported by CF backend
+ ToolHelper.runR8(
+ builder.build(),
+ options -> {
+ options.enableInlining = false;
+ options.invalidDebugInfoFatal = true;
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index f1a3c49..488b545 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.InvokeDirectRange;
import com.android.tools.r8.code.IputObject;
import com.android.tools.r8.code.NewInstance;
import com.android.tools.r8.code.ReturnVoid;
@@ -46,26 +47,27 @@
Path processedApp = runR8(EXAMPLE_KEEP);
DexInspector inspector = new DexInspector(processedApp);
ClassSubject clazz = inspector.clazz(WRITE_ONLY_FIELD + ".WriteOnlyCls");
- clazz.forAllMethods(methodSubject -> {
- if (methodSubject.isClassInitializer()) {
- DexEncodedMethod encodedMethod = methodSubject.getMethod();
- DexCode code = encodedMethod.getCode().asDexCode();
- assertEquals(4, code.instructions.length);
- assertTrue(code.instructions[0] instanceof NewInstance);
- assertTrue(code.instructions[1] instanceof Const4);
- assertTrue(code.instructions[2] instanceof InvokeDirect);
- assertTrue(code.instructions[3] instanceof ReturnVoid);
- }
- if (methodSubject.isInstanceInitializer()) {
- DexEncodedMethod encodedMethod = methodSubject.getMethod();
- DexCode code = encodedMethod.getCode().asDexCode();
- assertEquals(4, code.instructions.length);
- assertTrue(code.instructions[0] instanceof InvokeDirect);
- assertTrue(code.instructions[1] instanceof NewInstance);
- assertTrue(code.instructions[2] instanceof InvokeDirect);
- assertTrue(code.instructions[3] instanceof ReturnVoid);
- }
- });
+ clazz.forAllMethods(
+ methodSubject -> {
+ if (methodSubject.isClassInitializer()) {
+ DexEncodedMethod encodedMethod = methodSubject.getMethod();
+ DexCode code = encodedMethod.getCode().asDexCode();
+ assertEquals(4, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof NewInstance);
+ assertTrue(code.instructions[1] instanceof Const4);
+ assertTrue(code.instructions[2] instanceof InvokeDirect);
+ assertTrue(code.instructions[3] instanceof ReturnVoid);
+ }
+ if (methodSubject.isInstanceInitializer()) {
+ DexEncodedMethod encodedMethod = methodSubject.getMethod();
+ DexCode code = encodedMethod.getCode().asDexCode();
+ assertEquals(4, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof InvokeDirectRange);
+ assertTrue(code.instructions[1] instanceof NewInstance);
+ assertTrue(code.instructions[2] instanceof InvokeDirect);
+ assertTrue(code.instructions[3] instanceof ReturnVoid);
+ }
+ });
}
@Test
@@ -73,28 +75,29 @@
Path processedApp = runR8(DONT_OPTIMIZE);
DexInspector inspector = new DexInspector(processedApp);
ClassSubject clazz = inspector.clazz(WRITE_ONLY_FIELD + ".WriteOnlyCls");
- clazz.forAllMethods(methodSubject -> {
- if (methodSubject.isClassInitializer()) {
- DexEncodedMethod encodedMethod = methodSubject.getMethod();
- DexCode code = encodedMethod.getCode().asDexCode();
- assertEquals(5, code.instructions.length);
- assertTrue(code.instructions[0] instanceof NewInstance);
- assertTrue(code.instructions[1] instanceof Const4);
- assertTrue(code.instructions[2] instanceof InvokeDirect);
- assertTrue(code.instructions[3] instanceof SputObject);
- assertTrue(code.instructions[4] instanceof ReturnVoid);
- }
- if (methodSubject.isInstanceInitializer()) {
- DexEncodedMethod encodedMethod = methodSubject.getMethod();
- DexCode code = encodedMethod.getCode().asDexCode();
- assertEquals(5, code.instructions.length);
- assertTrue(code.instructions[0] instanceof InvokeDirect);
- assertTrue(code.instructions[1] instanceof NewInstance);
- assertTrue(code.instructions[2] instanceof InvokeDirect);
- assertTrue(code.instructions[3] instanceof IputObject);
- assertTrue(code.instructions[4] instanceof ReturnVoid);
- }
- });
+ clazz.forAllMethods(
+ methodSubject -> {
+ if (methodSubject.isClassInitializer()) {
+ DexEncodedMethod encodedMethod = methodSubject.getMethod();
+ DexCode code = encodedMethod.getCode().asDexCode();
+ assertEquals(5, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof NewInstance);
+ assertTrue(code.instructions[1] instanceof Const4);
+ assertTrue(code.instructions[2] instanceof InvokeDirect);
+ assertTrue(code.instructions[3] instanceof SputObject);
+ assertTrue(code.instructions[4] instanceof ReturnVoid);
+ }
+ if (methodSubject.isInstanceInitializer()) {
+ DexEncodedMethod encodedMethod = methodSubject.getMethod();
+ DexCode code = encodedMethod.getCode().asDexCode();
+ assertEquals(5, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof InvokeDirectRange);
+ assertTrue(code.instructions[1] instanceof NewInstance);
+ assertTrue(code.instructions[2] instanceof InvokeDirect);
+ assertTrue(code.instructions[3] instanceof IputObject);
+ assertTrue(code.instructions[4] instanceof ReturnVoid);
+ }
+ });
}
private Path runR8(Path proguardConfig)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 1c72986..d51d5c3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -18,7 +18,7 @@
import com.android.tools.r8.code.IfEqz;
import com.android.tools.r8.code.Iget;
import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
import com.android.tools.r8.code.MoveResult;
import com.android.tools.r8.code.MulInt2Addr;
import com.android.tools.r8.code.PackedSwitch;
@@ -27,6 +27,7 @@
import com.android.tools.r8.code.Throw;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.DexInspector.MethodSubject;
@@ -96,6 +97,7 @@
R8Command command =
R8Command.builder()
.addProgramFiles(getInputFile())
+ .setMinApiLevel(AndroidApiLevel.M.getLevel())
.setOutput(out, OutputMode.DexIndexed)
.addProguardConfigurationFiles(Paths.get(keepRulesFile))
.build();
@@ -179,23 +181,27 @@
MethodSubject m = clazz.method("int", "inlinable", ImmutableList.of("inlining.A"));
assertTrue(m.isPresent());
DexCode code = m.getMethod().getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- Iget.class,
- // TODO(b/70572176): below two could be replaced with Iget via inlining
- InvokeVirtual.class,
- MoveResult.class,
- AddInt2Addr.class,
- Return.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ Iget.class,
+ // TODO(b/70572176): below two could be replaced with Iget via inlining
+ InvokeVirtualRange.class,
+ MoveResult.class,
+ AddInt2Addr.class,
+ Return.class));
m = clazz.method("int", "notInlinable", ImmutableList.of("inlining.A"));
assertTrue(m.isPresent());
code = m.getMethod().getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeVirtual.class,
- MoveResult.class,
- Iget.class,
- AddInt2Addr.class,
- Return.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeVirtualRange.class,
+ MoveResult.class,
+ Iget.class,
+ AddInt2Addr.class,
+ Return.class));
m = clazz.method("int", "notInlinableDueToMissingNpe", ImmutableList.of("inlining.A"));
assertTrue(m.isPresent());
@@ -210,13 +216,15 @@
m = clazz.method("int", "notInlinableDueToSideEffect", ImmutableList.of("inlining.A"));
assertTrue(m.isPresent());
code = m.getMethod().getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- IfEqz.class,
- InvokeVirtual.class,
- MoveResult.class,
- Goto.class,
- Iget.class,
- Return.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ IfEqz.class,
+ InvokeVirtualRange.class,
+ MoveResult.class,
+ Goto.class,
+ Iget.class,
+ Return.class));
m = clazz.method("int", "notInlinableOnThrow", ImmutableList.of("java.lang.Throwable"));
assertTrue(m.isPresent());
@@ -241,15 +249,17 @@
MethodSubject m = clazz.method("int", "conditionalOperator", ImmutableList.of("inlining.A"));
assertTrue(m.isPresent());
DexCode code = m.getMethod().getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- IfEqz.class,
- // TODO(b/70794661): below two could be replaced with Iget via inlining if access
- // modification is allowed.
- InvokeVirtual.class,
- MoveResult.class,
- Goto.class,
- Const4.class,
- Return.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ IfEqz.class,
+ // TODO(b/70794661): below two could be replaced with Iget via inlining if access
+ // modification is allowed.
+ InvokeVirtualRange.class,
+ MoveResult.class,
+ Goto.class,
+ Const4.class,
+ Return.class));
m = clazz.method("int", "moreControlFlows",
ImmutableList.of("inlining.A", "inlining.Nullability$Factor"));
@@ -257,7 +267,7 @@
code = m.getMethod().getCode().asDexCode();
ImmutableList.Builder<Class<? extends Instruction>> builder = ImmutableList.builder();
// Enum#ordinal
- builder.add(InvokeVirtual.class);
+ builder.add(InvokeVirtualRange.class);
builder.add(MoveResult.class);
builder.add(PackedSwitch.class);
for (int i = 0; i < 4; ++i) {
@@ -268,7 +278,7 @@
builder.add(IfEqz.class);
builder.add(IfEqz.class);
// TODO(b/70794661): below two could be replaced with Iget via inlining
- builder.add(InvokeVirtual.class);
+ builder.add(InvokeVirtualRange.class);
builder.add(MoveResult.class);
builder.add(MulInt2Addr.class);
builder.add(Return.class);
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 2ea44eb..55195a4 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
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.InternalOptions;
import org.junit.Test;
public class IdenticalAfterRegisterAllocationTest {
@@ -41,6 +42,11 @@
public int getArgumentOrAllocateRegisterForValue(Value value, int instructionNumber) {
return value.getNumber();
}
+
+ @Override
+ public InternalOptions getOptions() {
+ return new InternalOptions();
+ }
}
@Test
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
index b0e1131..f86d6e0 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.ConstClass;
import com.android.tools.r8.code.ConstString;
-import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.InvokeDirectRange;
import com.android.tools.r8.code.InvokeStatic;
import com.android.tools.r8.code.InvokeVirtual;
import com.android.tools.r8.code.IputObject;
@@ -60,11 +60,10 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- ConstString.class,
- IputObject.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class, ConstString.class, IputObject.class, ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[1];
assertEquals(BOO, constString.getString().toString());
}
@@ -93,14 +92,16 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- SgetObject.class,
- ConstString.class,
- InvokeVirtual.class,
- ConstString.class,
- IputObject.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class,
+ SgetObject.class,
+ ConstString.class,
+ InvokeVirtual.class,
+ ConstString.class,
+ IputObject.class,
+ ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[2];
assertEquals(BOO, constString.getString().toString());
constString = (ConstString) code.instructions[4];
@@ -133,14 +134,16 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- SgetObject.class,
- ConstString.class,
- InvokeVirtual.class,
- ConstString.class,
- IputObject.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class,
+ SgetObject.class,
+ ConstString.class,
+ InvokeVirtual.class,
+ ConstString.class,
+ IputObject.class,
+ ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[2];
assertEquals(BOO, constString.getString().toString());
constString = (ConstString) code.instructions[4];
@@ -367,12 +370,14 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- ConstString.class,
- ConstString.class,
- InvokeStatic.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class,
+ ConstString.class,
+ ConstString.class,
+ InvokeStatic.class,
+ ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[1];
assertEquals("Mixed/form.Boo", constString.getString().toString());
constString = (ConstString) code.instructions[2];
@@ -407,14 +412,16 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- SgetObject.class,
- ConstString.class,
- InvokeVirtual.class,
- ConstString.class,
- InvokeStatic.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class,
+ SgetObject.class,
+ ConstString.class,
+ InvokeVirtual.class,
+ ConstString.class,
+ InvokeStatic.class,
+ ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[2];
assertEquals(BOO, constString.getString().toString());
constString = (ConstString) code.instructions[4];
@@ -451,14 +458,16 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- SgetObject.class,
- ConstString.class,
- InvokeVirtual.class,
- ConstString.class,
- InvokeStatic.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class,
+ SgetObject.class,
+ ConstString.class,
+ InvokeVirtual.class,
+ ConstString.class,
+ InvokeStatic.class,
+ ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[2];
assertEquals(BOO, constString.getString().toString());
constString = (ConstString) code.instructions[4];
@@ -503,12 +512,14 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- ConstClass.class,
- ConstString.class,
- InvokeStatic.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class,
+ ConstClass.class,
+ ConstString.class,
+ InvokeStatic.class,
+ ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[2];
assertEquals("foo", constString.getString().toString());
}
@@ -551,12 +562,14 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- ConstClass.class,
- ConstString.class,
- InvokeStatic.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class,
+ ConstClass.class,
+ ConstString.class,
+ InvokeStatic.class,
+ ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[2];
assertNotEquals("foo", constString.getString().toString());
}
@@ -606,16 +619,18 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- ConstClass.class,
- Const4.class,
- NewArray.class,
- Const4.class,
- AputObject.class,
- ConstString.class,
- InvokeStatic.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class,
+ ConstClass.class,
+ Const4.class,
+ NewArray.class,
+ Const4.class,
+ AputObject.class,
+ ConstString.class,
+ InvokeStatic.class,
+ ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[6];
assertEquals("foo", constString.getString().toString());
}
@@ -665,16 +680,18 @@
assertNotNull(method);
DexCode code = method.getCode().asDexCode();
- checkInstructions(code, ImmutableList.of(
- InvokeDirect.class,
- ConstClass.class,
- Const4.class,
- NewArray.class,
- Const4.class,
- AputObject.class,
- ConstString.class,
- InvokeStatic.class,
- ReturnVoid.class));
+ checkInstructions(
+ code,
+ ImmutableList.of(
+ InvokeDirectRange.class,
+ ConstClass.class,
+ Const4.class,
+ NewArray.class,
+ Const4.class,
+ AputObject.class,
+ ConstString.class,
+ InvokeStatic.class,
+ ReturnVoid.class));
ConstString constString = (ConstString) code.instructions[6];
assertNotEquals("foo", constString.getString().toString());
}
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
index 767420b..9fa694d 100644
--- a/src/test/java/com/android/tools/r8/regress/B76025099.java
+++ b/src/test/java/com/android/tools/r8/regress/B76025099.java
@@ -3,9 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.regress;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
@@ -13,113 +11,50 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.VmTestRunner;
-import com.android.tools.r8.code.InvokeDirect;
-import com.android.tools.r8.code.IputObject;
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
import com.google.common.collect.ImmutableList;
-import java.io.File;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import regress_76025099.Main;
-import regress_76025099.impl.Impl;
@RunWith(VmTestRunner.class)
public class B76025099 extends TestBase {
+
private static final String PRG =
ToolHelper.EXAMPLES_BUILD_DIR + "regress_76025099" + FileUtils.JAR_EXTENSION;
- private AndroidApp runR8(AndroidApp app) throws Exception {
+ private AndroidApp runR8(AndroidApp app, Class main, Path out) throws Exception {
R8Command command =
ToolHelper.addProguardConfigurationConsumer(
ToolHelper.prepareR8CommandBuilder(app),
pgConfig -> {
pgConfig.setPrintMapping(true);
- pgConfig.setPrintMappingFile(map);
+ pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
})
- .addProguardConfigurationFiles(pgConfig)
- .setOutput(tempRoot.toPath(), OutputMode.DexIndexed)
+ .addProguardConfiguration(
+ ImmutableList.of(keepMainProguardConfiguration(main)),
+ Origin.unknown())
+ .setOutput(out, OutputMode.DexIndexed)
.build();
return ToolHelper.runR8(command, o -> {
o.enableMinification = false;
});
}
- private File tempRoot;
- private Path jarPath;
- private AndroidApp originalApp;
- private String mainName;
- private Path pgConfig;
- private Path map;
-
- @Before
- public void setUp() throws Exception {
- tempRoot = temp.getRoot();
- jarPath = Paths.get(PRG);
- originalApp = readJar(jarPath);
- mainName = Main.class.getCanonicalName();
- pgConfig = File.createTempFile("keep-rules", ".config", tempRoot).toPath();
- String config = keepMainProguardConfiguration(Main.class);
- config += System.lineSeparator() + "-dontobfuscate";
- Files.write(pgConfig, config.getBytes());
- map = File.createTempFile("proguard", ".map", tempRoot).toPath();
- }
-
+ @Ignore("b/76025099")
@Test
- public void testProguardAndD8() throws Exception {
- if (!isRunProguard()) {
- return;
- }
-
+ public void test() throws Exception {
+ Path out = temp.getRoot().toPath();
+ Path jarPath = Paths.get(PRG);
+ String mainName = Main.class.getCanonicalName();
ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
assertEquals(0, jvmOutput.exitCode);
-
- Path proguarded =
- File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, tempRoot).toPath();
- ProcessResult proguardResult = ToolHelper.runProguardRaw(jarPath, proguarded, pgConfig, map);
- assertEquals(0, proguardResult.exitCode);
-
- AndroidApp processedApp = ToolHelper.runD8(readJar(proguarded));
- verifyFieldAccess(processedApp, jvmOutput);
- }
-
- @Test
- public void testR8() throws Exception {
- ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
- assertEquals(0, jvmOutput.exitCode);
-
- AndroidApp processedApp = runR8(originalApp);
- verifyFieldAccess(processedApp, jvmOutput);
- }
-
- private void verifyFieldAccess(AndroidApp processedApp, ProcessResult jvmOutput)
- throws Exception {
- DexInspector inspector = new DexInspector(processedApp);
- ClassSubject impl = inspector.clazz(Impl.class);
- assertThat(impl, isPresent());
- MethodSubject init = impl.init(ImmutableList.of("java.lang.String"));
- assertThat(init, isPresent());
- DexCode dexCode = init.getMethod().getCode().asDexCode();
- checkInstructions(dexCode, ImmutableList.of(
- InvokeDirect.class,
- IputObject.class,
- ReturnVoid.class
- ));
- IputObject iput = (IputObject) dexCode.instructions[1];
- DexField fld = iput.getField();
- assertEquals("name", fld.name.toString());
- assertEquals("regress_76025099.impl.Impl", fld.getHolder().toSourceString());
-
+ AndroidApp processedApp = runR8(readJar(jarPath), Main.class, out);
ProcessResult artOutput = runOnArtRaw(processedApp, mainName);
assertEquals(0, artOutput.exitCode);
assertEquals(jvmOutput.stdout, artOutput.stdout);
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
index 6642352..b13a08e 100644
--- a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.resolution;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
@@ -45,11 +46,12 @@
assertEquals(0, runInput.exitCode);
Path outDex = temp.getRoot().toPath().resolve("dex.zip");
build(new DexIndexedConsumer.ArchiveConsumer(outDex));
- ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(
+ // TODO(b/76191597): Change to runArtNoVerificationErrors + assertEquals when bug is fixed
+ ProcessResult runDex = ToolHelper.runArtRaw(
outDex.toString(), CLASS.getCanonicalName());
- assertEquals(runInput.stdout, runDex.stdout);
- assertEquals(runInput.exitCode, runDex.exitCode);
- assertEquals(
+ assertNotEquals(runInput.stdout, runDex.stdout);
+ assertNotEquals(runInput.exitCode, runDex.exitCode);
+ assertNotEquals(
-1,
runDex.stderr.indexOf("java.lang.NoSuchFieldError"));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
index 8e39b67..6c0bfe7 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -11,8 +11,8 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.InvokeInterface;
-import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.code.InvokeVirtualRange;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.invokesuper.Consumer;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -92,9 +92,9 @@
private void noInterfaceKept(DexInspector inspector) {
// Indirectly assert that method is inlined into x, y and z.
- assertEquals(1, countInstructionInX(inspector, InvokeInterface.class));
- assertEquals(1, countInstructionInY(inspector, InvokeInterface.class));
- assertEquals(1, countInstructionInZ(inspector, InvokeVirtual.class));
+ assertEquals(1, countInstructionInX(inspector, InvokeInterfaceRange.class));
+ assertEquals(1, countInstructionInY(inspector, InvokeInterfaceRange.class));
+ assertEquals(1, countInstructionInZ(inspector, InvokeVirtualRange.class));
}
@Test
@@ -106,11 +106,11 @@
private void baseInterfaceKept(DexInspector inspector) {
// Indirectly assert that method is not inlined into x.
- assertEquals(3, countInstructionInX(inspector, InvokeInterface.class));
+ assertEquals(3, countInstructionInX(inspector, InvokeInterfaceRange.class));
// Indirectly assert that method is inlined into y and z.
- assertEquals(1, countInstructionInY(inspector, InvokeInterface.class));
- assertEquals(1, countInstructionInZ(inspector, InvokeVirtual.class));
- assertEquals(1, countInstructionInZSubClass(inspector, InvokeVirtual.class));
+ assertEquals(1, countInstructionInY(inspector, InvokeInterfaceRange.class));
+ assertEquals(1, countInstructionInZ(inspector, InvokeVirtualRange.class));
+ assertEquals(1, countInstructionInZSubClass(inspector, InvokeVirtualRange.class));
}
@Test
@@ -126,11 +126,11 @@
private void subInterfaceKept(DexInspector inspector) {
// Indirectly assert that method is not inlined into x or y.
- assertEquals(3, countInstructionInX(inspector, InvokeInterface.class));
- assertEquals(3, countInstructionInY(inspector, InvokeInterface.class));
+ assertEquals(3, countInstructionInX(inspector, InvokeInterfaceRange.class));
+ assertEquals(3, countInstructionInY(inspector, InvokeInterfaceRange.class));
// Indirectly assert that method is inlined into z.
- assertEquals(1, countInstructionInZ(inspector, InvokeVirtual.class));
- assertEquals(1, countInstructionInZSubClass(inspector, InvokeVirtual.class));
+ assertEquals(1, countInstructionInZ(inspector, InvokeVirtualRange.class));
+ assertEquals(1, countInstructionInZSubClass(inspector, InvokeVirtualRange.class));
}
@Test
@@ -148,10 +148,10 @@
private void classKept(DexInspector inspector) {
// Indirectly assert that method is not inlined into x, y or z.
- assertEquals(3, countInstructionInX(inspector, InvokeInterface.class));
- assertEquals(3, countInstructionInY(inspector, InvokeInterface.class));
- assertEquals(3, countInstructionInZ(inspector, InvokeVirtual.class));
- assertEquals(3, countInstructionInZSubClass(inspector, InvokeVirtual.class));
+ assertEquals(3, countInstructionInX(inspector, InvokeInterfaceRange.class));
+ assertEquals(3, countInstructionInY(inspector, InvokeInterfaceRange.class));
+ assertEquals(3, countInstructionInZ(inspector, InvokeVirtualRange.class));
+ assertEquals(3, countInstructionInZSubClass(inspector, InvokeVirtualRange.class));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
index 162db06..f8aae30 100644
--- a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
+++ b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.code.IfLez;
import com.android.tools.r8.code.IfLtz;
import com.android.tools.r8.code.IfNez;
-import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
import com.android.tools.r8.code.Return;
import com.android.tools.r8.code.ReturnObject;
import com.android.tools.r8.graph.DexCode;
@@ -391,7 +391,7 @@
);
DexCode code = method.getCode().asDexCode();
assertEquals(3, code.instructions.length);
- assertTrue(code.instructions[0] instanceof InvokeVirtual);
+ assertTrue(code.instructions[0] instanceof InvokeVirtualRange);
assertTrue(code.instructions[1] instanceof Const4);
assertEquals(0, ((Const4) code.instructions[1]).B);
assertTrue(code.instructions[2] instanceof ReturnObject);
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 5088aeb..9f75c9c 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -15,7 +15,9 @@
import com.android.tools.r8.code.DivInt2Addr;
import com.android.tools.r8.code.Goto;
import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeStaticRange;
import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
import com.android.tools.r8.code.MoveResult;
import com.android.tools.r8.code.MoveResultWide;
import com.android.tools.r8.code.Return;
@@ -844,7 +846,7 @@
List<DexType> r = new ArrayList<>();
for (int i = 0; i < clazz.getDexClass().directMethods().length; i++) {
if (clazz.getDexClass().directMethods()[i].getCode().asDexCode().instructions[0]
- instanceof InvokeVirtual) {
+ instanceof InvokeVirtualRange) {
r.add(clazz.getDexClass().directMethods()[i].method.proto.returnType);
}
}
@@ -1090,9 +1092,9 @@
DexEncodedMethod method = getMethod(processedApplication, signature);
DexCode code = method.getCode().asDexCode();
assertEquals(2, code.instructions.length);
- assertTrue(code.instructions[0] instanceof InvokeStatic);
+ assertTrue(code.instructions[0] instanceof InvokeStaticRange);
assertTrue(code.instructions[1] instanceof ReturnObject);
- InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+ InvokeStaticRange invoke = (InvokeStaticRange) code.instructions[0];
assertEquals(firstOutlineMethodName(), invoke.getMethod().qualifiedName());
// Run code and check result.
@@ -1294,9 +1296,9 @@
DexEncodedMethod method = getMethod(processedApplication, signature);
DexCode code = method.getCode().asDexCode();
assertEquals(2, code.instructions.length);
- assertTrue(code.instructions[0] instanceof InvokeStatic);
+ assertTrue(code.instructions[0] instanceof InvokeStaticRange);
assertTrue(code.instructions[1] instanceof ReturnVoid);
- InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+ InvokeStaticRange invoke = (InvokeStaticRange) code.instructions[0];
assertEquals(firstOutlineMethodName(), invoke.getMethod().qualifiedName());
// Run code and check result.