Merge commit '9a4ecac0' into dev-release
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 646a96a..d96a5a1 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -163,8 +163,8 @@
}
public boolean isShrinking() {
- // Disable for release.
- return false;
+ // Answers true if keep rules, even empty, are provided.
+ return !proguardConfigStrings.isEmpty() || !proguardConfigFiles.isEmpty();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 6c3efbe..1eb4ec9 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -27,12 +27,16 @@
* @return Major version or -1 for an unreleased build.
*/
public static int getMajorVersion() {
- if (LABEL.equals("master")) {
+ return getMajorVersion(LABEL);
+ }
+
+ static int getMajorVersion(String label) {
+ if (label.equals("master")) {
return -1;
}
int start = 0;
- int end = LABEL.indexOf('.');
- return Integer.parseInt(LABEL.substring(start, end));
+ int end = label.indexOf('.');
+ return Integer.parseInt(label.substring(start, end));
}
/**
@@ -41,12 +45,16 @@
* @return Minor version or -1 for an unreleased build.
*/
public static int getMinorVersion() {
- if (LABEL.equals("master")) {
+ return getMinorVersion(LABEL);
+ }
+
+ static int getMinorVersion(String label) {
+ if (label.equals("master")) {
return -1;
}
- int start = LABEL.indexOf('.') + 1;
- int end = LABEL.indexOf('.', start);
- return Integer.parseInt(LABEL.substring(start, end));
+ int start = label.indexOf('.') + 1;
+ int end = label.indexOf('.', start);
+ return Integer.parseInt(label.substring(start, end));
}
/**
@@ -55,13 +63,17 @@
* @return Patch version or -1 for an unreleased build.
*/
public static int getPatchVersion() {
- if (LABEL.equals("master")) {
+ return getPatchVersion(LABEL);
+ }
+
+ static int getPatchVersion(String label) {
+ if (label.equals("master")) {
return -1;
}
- int skip = LABEL.indexOf('.') + 1;
- int start = LABEL.indexOf('.', skip) + 1;
- int end = LABEL.indexOf('.', start);
- return Integer.parseInt(LABEL.substring(start, end));
+ int skip = label.indexOf('.') + 1;
+ int start = label.indexOf('.', skip) + 1;
+ int end = label.indexOf('-', start);
+ return Integer.parseInt(label.substring(start, end != -1 ? end : label.length()));
}
/**
@@ -71,12 +83,16 @@
* unreleased build.
*/
public static String getPreReleaseString() {
- if (LABEL.equals("master")) {
+ return getPreReleaseString(LABEL);
+ }
+
+ static String getPreReleaseString(String label) {
+ if (label.equals("master")) {
return null;
}
- int start = LABEL.indexOf('-') + 1;
+ int start = label.indexOf('-') + 1;
if (start > 0) {
- return LABEL.substring(start);
+ return label.substring(start);
}
return "";
}
@@ -87,8 +103,10 @@
* @return True if the build is not a release or if it is a development release.
*/
public static boolean isDevelopmentVersion() {
- return LABEL.equals("master")
- || LABEL.endsWith("-dev")
- || VersionProperties.INSTANCE.isEngineering();
+ return isDevelopmentVersion(LABEL, VersionProperties.INSTANCE.isEngineering());
+ }
+
+ static boolean isDevelopmentVersion(String label, boolean isEngineering) {
+ return label.equals("master") || label.endsWith("-dev") || isEngineering;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 72e372a..c340ae7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -626,6 +626,14 @@
return accessFlags.isAbstract();
}
+ public boolean isFinal() {
+ return accessFlags.isFinal();
+ }
+
+ public boolean isEffectivelyFinal(AppView<?> appView) {
+ return isFinal();
+ }
+
public boolean isInterface() {
return accessFlags.isInterface();
}
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 1174f5e..40c60ac 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -344,6 +344,7 @@
new StringBuildingMethods(stringBuilderType);
public final StringBuildingMethods stringBufferMethods =
new StringBuildingMethods(stringBufferType);
+ public final BooleanMembers booleanMembers = new BooleanMembers();
public final ObjectsMethods objectsMethods = new ObjectsMethods();
public final ObjectMethods objectMethods = new ObjectMethods();
public final StringMethods stringMethods = new StringMethods();
@@ -552,7 +553,7 @@
.build();
public final Set<DexType> libraryClassesWithoutStaticInitialization =
- ImmutableSet.of(enumType, objectType, stringBufferType, stringBuilderType);
+ ImmutableSet.of(boxedBooleanType, enumType, objectType, stringBufferType, stringBuilderType);
private boolean skipNameValidationForTesting = false;
@@ -568,6 +569,20 @@
return dexMethod == metafactoryMethod || dexMethod == metafactoryAltMethod;
}
+ public class BooleanMembers {
+
+ public final DexField FALSE = createField(boxedBooleanType, boxedBooleanType, "FALSE");
+ public final DexField TRUE = createField(boxedBooleanType, boxedBooleanType, "TRUE");
+ public final DexField TYPE = createField(boxedBooleanType, classType, "TYPE");
+
+ public final DexMethod booleanValue =
+ createMethod(boxedBooleanType, createProto(booleanType), "booleanValue");
+ public final DexMethod valueOf =
+ createMethod(boxedBooleanType, createProto(boxedBooleanType, booleanType), "valueOf");
+
+ private BooleanMembers() {}
+ }
+
public class LongMethods {
public final DexMethod compare;
@@ -806,7 +821,7 @@
* E.g. for Boolean https://docs.oracle.com/javase/8/docs/api/java/lang/Boolean.html#TYPE.
*/
public class PrimitiveTypesBoxedTypeFields {
- public final DexField booleanTYPE;
+
public final DexField byteTYPE;
public final DexField charTYPE;
public final DexField shortTYPE;
@@ -818,7 +833,6 @@
private final Map<DexField, DexType> boxedFieldTypeToPrimitiveType;
private PrimitiveTypesBoxedTypeFields() {
- booleanTYPE = createField(boxedBooleanType, classType, "TYPE");
byteTYPE = createField(boxedByteType, classType, "TYPE");
charTYPE = createField(boxedCharType, classType, "TYPE");
shortTYPE = createField(boxedShortType, classType, "TYPE");
@@ -829,7 +843,7 @@
boxedFieldTypeToPrimitiveType =
ImmutableMap.<DexField, DexType>builder()
- .put(booleanTYPE, booleanType)
+ .put(booleanMembers.TYPE, booleanType)
.put(byteTYPE, byteType)
.put(charTYPE, charType)
.put(shortTYPE, shortType)
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 92a4963..5a89dcf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -228,6 +229,26 @@
return type.toSourceString();
}
+ /**
+ * Returns true if this class is final, or it is a non-pinned program class with no instantiated
+ * subtypes.
+ */
+ @Override
+ public boolean isEffectivelyFinal(AppView<?> appView) {
+ if (isFinal()) {
+ return true;
+ }
+ if (appView.enableWholeProgramOptimizations()) {
+ assert appView.appInfo().hasLiveness();
+ AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
+ if (appInfo.isPinned(type)) {
+ return false;
+ }
+ return !appInfo.hasSubtypes(type) || !appInfo.isInstantiatedIndirectly(type);
+ }
+ return false;
+ }
+
@Override
public boolean isProgramClass() {
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 76dd396..4d8d00d 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -73,6 +73,7 @@
import java.util.function.BiFunction;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
@@ -798,6 +799,8 @@
instructions.add(
new CfConstMethodHandle(
DexMethodHandle.fromAsmHandle((Handle) cst, application, method.holder)));
+ } else if (cst instanceof ConstantDynamic) {
+ throw new CompilationError("Unsupported dynamic constant: " + cst.toString());
} else {
throw new CompilationError("Unsupported constant: " + cst.toString());
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
index 4adc57e..3ad41d4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
@@ -89,7 +89,7 @@
}
@Override
- public ReferenceTypeLatticeElement getOrCreateVariant(Nullability nullability) {
+ public ClassTypeLatticeElement getOrCreateVariant(Nullability nullability) {
ClassTypeLatticeElement variant = variants.get(nullability);
if (variant != null) {
return variant;
@@ -116,6 +116,11 @@
}
@Override
+ public ClassTypeLatticeElement asMeetWithNotNull() {
+ return getOrCreateVariant(nullability.meet(Nullability.definitelyNotNull()));
+ }
+
+ @Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(nullability);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index 28ac1a2..64401fd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -34,6 +34,11 @@
return this;
}
+ public boolean getBooleanValue() {
+ assert value == 0 || value == 1;
+ return value != 0;
+ }
+
public long getValue() {
return value;
}
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 b2273e9..d71b495 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
@@ -559,30 +559,13 @@
}
public int numberInstructions(int nextInstructionNumber) {
- return numberInstructions(nextInstructionNumber, INSTRUCTION_NUMBER_DELTA);
- }
-
- public int numberInstructions(int nextInstructionNumber, int increment) {
for (Instruction instruction : instructions) {
instruction.setNumber(nextInstructionNumber);
- nextInstructionNumber += increment;
+ nextInstructionNumber += INSTRUCTION_NUMBER_DELTA;
}
return nextInstructionNumber;
}
- public void clearInstructionNumbers() {
- for (Instruction instruction : instructions) {
- instruction.clearNumber();
- }
- }
-
- public boolean hasNoInstructionNumbers() {
- for (Instruction instruction : instructions) {
- assert instruction.getNumber() == -1;
- }
- return true;
- }
-
public LinkedList<Instruction> getInstructions() {
return instructions;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 708e947..085c936 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -4,10 +4,12 @@
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -218,6 +220,55 @@
}
@Override
+ public void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value) {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+
+ assert !current.hasOutValue() || current.outValue().getTypeLattice().isInt();
+
+ // Replace the instruction by const-number.
+ ConstNumber constNumber = code.createIntConstant(value, current.getLocalInfo());
+ for (Value inValue : current.inValues()) {
+ if (inValue.hasLocalInfo()) {
+ // Add this value as a debug value to avoid changing its live range.
+ constNumber.addDebugValue(inValue);
+ }
+ }
+ replaceCurrentInstruction(constNumber);
+ }
+
+ @Override
+ public void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues) {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+
+ // Replace the instruction by static-get.
+ TypeLatticeElement newType = TypeLatticeElement.fromDexType(field.type, maybeNull(), appView);
+ TypeLatticeElement oldType = current.hasOutValue() ? current.outValue().getTypeLattice() : null;
+ Value value = code.createValue(newType, current.getLocalInfo());
+ StaticGet staticGet = new StaticGet(value, field);
+ for (Value inValue : current.inValues()) {
+ if (inValue.hasLocalInfo()) {
+ // Add this value as a debug value to avoid changing its live range.
+ staticGet.addDebugValue(inValue);
+ }
+ }
+ replaceCurrentInstruction(staticGet);
+
+ // Update affected values.
+ if (value.hasAnyUsers() && !newType.equals(oldType)) {
+ affectedValues.addAll(value.affectedValues());
+ }
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithSubtyping> appView,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 805aad9..1a34adf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -763,7 +763,13 @@
private boolean consistentMetadata() {
for (Instruction instruction : instructions()) {
- if (instruction.isCheckCast()) {
+ if (instruction.isAdd()) {
+ assert metadata.mayHaveAdd() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has an add";
+ } else if (instruction.isAnd()) {
+ assert metadata.mayHaveAnd() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has an and";
+ } else if (instruction.isCheckCast()) {
assert metadata.mayHaveCheckCast()
: "IR metadata should indicate that code has a check-cast";
} else if (instruction.isConstNumber()) {
@@ -778,6 +784,9 @@
} else if (instruction.isDexItemBasedConstString()) {
assert metadata.mayHaveDexItemBasedConstString()
: "IR metadata should indicate that code has a dex-item-based-const-string";
+ } else if (instruction.isDiv()) {
+ assert metadata.mayHaveDiv() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has a div";
} else if (instruction.isInstanceGet()) {
assert metadata.mayHaveInstanceGet()
: "IR metadata should indicate that code has an instance-get";
@@ -808,9 +817,24 @@
} else if (instruction.isInvokeVirtual()) {
assert metadata.mayHaveInvokeVirtual()
: "IR metadata should indicate that code has an invoke-virtual";
+ } else if (instruction.isOr()) {
+ assert metadata.mayHaveOr() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has an or";
} else if (instruction.isMonitor()) {
assert metadata.mayHaveMonitorInstruction()
: "IR metadata should indicate that code has a monitor instruction";
+ } else if (instruction.isMul()) {
+ assert metadata.mayHaveMul() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has a mul";
+ } else if (instruction.isRem()) {
+ assert metadata.mayHaveRem() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has a rem";
+ } else if (instruction.isShl()) {
+ assert metadata.mayHaveShl() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has a shl";
+ } else if (instruction.isShr()) {
+ assert metadata.mayHaveShr() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has a shr";
} else if (instruction.isStaticGet()) {
assert metadata.mayHaveStaticGet()
: "IR metadata should indicate that code has a static-get";
@@ -820,6 +844,15 @@
} else if (instruction.isStringSwitch()) {
assert metadata.mayHaveStringSwitch()
: "IR metadata should indicate that code has a string-switch";
+ } else if (instruction.isSub()) {
+ assert metadata.mayHaveSub() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has a sub";
+ } else if (instruction.isUshr()) {
+ assert metadata.mayHaveUshr() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has an ushr";
+ } else if (instruction.isXor()) {
+ assert metadata.mayHaveXor() && metadata.mayHaveArithmeticOrLogicalBinop()
+ : "IR metadata should indicate that code has an xor";
}
}
return true;
@@ -958,25 +991,6 @@
return blocks;
}
- public void numberInstructionsPerBlock() {
- for (BasicBlock block : blocks) {
- block.numberInstructions(0, 1);
- }
- }
-
- public void clearInstructionNumbers() {
- for (BasicBlock block : blocks) {
- block.clearInstructionNumbers();
- }
- }
-
- public boolean hasNoInstructionNumbers() {
- for (BasicBlock block : blocks) {
- assert block.hasNoInstructionNumbers();
- }
- return true;
- }
-
public int numberRemainingInstructions() {
for (Instruction instruction : instructions()) {
if (instruction.getNumber() == -1) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index 60e06c6..fdea0f6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ListIterator;
@@ -37,6 +38,22 @@
}
@Override
+ public void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value) {
+ instructionIterator.replaceCurrentInstructionWithConstInt(appView, code, value);
+ }
+
+ @Override
+ public void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues) {
+ instructionIterator.replaceCurrentInstructionWithStaticGet(
+ appView, code, field, affectedValues);
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithSubtyping> appView,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
index f6054e1..c4d55d8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -57,6 +57,14 @@
second |= metadata.second;
}
+ public boolean mayHaveAdd() {
+ return get(Opcodes.ADD);
+ }
+
+ public boolean mayHaveAnd() {
+ return get(Opcodes.AND);
+ }
+
public boolean mayHaveCheckCast() {
return get(Opcodes.CHECK_CAST);
}
@@ -77,15 +85,19 @@
return get(Opcodes.DEX_ITEM_BASED_CONST_STRING);
}
+ public boolean mayHaveDiv() {
+ return get(Opcodes.DIV);
+ }
+
public boolean mayHaveFieldGet() {
return mayHaveInstanceGet() || mayHaveStaticGet();
}
public boolean mayHaveFieldInstruction() {
- assert Opcodes.INSTANCE_GET <= 64;
- assert Opcodes.INSTANCE_PUT <= 64;
- assert Opcodes.STATIC_GET <= 64;
- assert Opcodes.STATIC_PUT <= 64;
+ assert Opcodes.INSTANCE_GET < 64;
+ assert Opcodes.INSTANCE_PUT < 64;
+ assert Opcodes.STATIC_GET < 64;
+ assert Opcodes.STATIC_PUT < 64;
long mask =
(1L << Opcodes.INSTANCE_GET)
| (1L << Opcodes.INSTANCE_PUT)
@@ -126,12 +138,12 @@
@SuppressWarnings("ConstantConditions")
public boolean mayHaveInvokeMethod() {
- assert Opcodes.INVOKE_DIRECT <= 64;
- assert Opcodes.INVOKE_INTERFACE <= 64;
- assert Opcodes.INVOKE_POLYMORPHIC <= 64;
- assert Opcodes.INVOKE_STATIC <= 64;
- assert Opcodes.INVOKE_SUPER <= 64;
- assert Opcodes.INVOKE_VIRTUAL <= 64;
+ assert Opcodes.INVOKE_DIRECT < 64;
+ assert Opcodes.INVOKE_INTERFACE < 64;
+ assert Opcodes.INVOKE_POLYMORPHIC < 64;
+ assert Opcodes.INVOKE_STATIC < 64;
+ assert Opcodes.INVOKE_SUPER < 64;
+ assert Opcodes.INVOKE_VIRTUAL < 64;
long mask =
(1L << Opcodes.INVOKE_DIRECT)
| (1L << Opcodes.INVOKE_INTERFACE)
@@ -152,10 +164,10 @@
@SuppressWarnings("ConstantConditions")
public boolean mayHaveInvokeMethodWithReceiver() {
- assert Opcodes.INVOKE_DIRECT <= 64;
- assert Opcodes.INVOKE_INTERFACE <= 64;
- assert Opcodes.INVOKE_SUPER <= 64;
- assert Opcodes.INVOKE_VIRTUAL <= 64;
+ assert Opcodes.INVOKE_DIRECT < 64;
+ assert Opcodes.INVOKE_INTERFACE < 64;
+ assert Opcodes.INVOKE_SUPER < 64;
+ assert Opcodes.INVOKE_VIRTUAL < 64;
long mask =
(1L << Opcodes.INVOKE_DIRECT)
| (1L << Opcodes.INVOKE_INTERFACE)
@@ -190,6 +202,26 @@
return get(Opcodes.MONITOR);
}
+ public boolean mayHaveMul() {
+ return get(Opcodes.MUL);
+ }
+
+ public boolean mayHaveOr() {
+ return get(Opcodes.OR);
+ }
+
+ public boolean mayHaveRem() {
+ return get(Opcodes.REM);
+ }
+
+ public boolean mayHaveShl() {
+ return get(Opcodes.SHL);
+ }
+
+ public boolean mayHaveShr() {
+ return get(Opcodes.SHR);
+ }
+
public boolean mayHaveStaticGet() {
return get(Opcodes.STATIC_GET);
}
@@ -202,8 +234,58 @@
return get(Opcodes.STRING_SWITCH);
}
+ public boolean mayHaveSub() {
+ return get(Opcodes.SUB);
+ }
+
+ public boolean mayHaveUshr() {
+ return get(Opcodes.USHR);
+ }
+
+ public boolean mayHaveXor() {
+ return get(Opcodes.XOR);
+ }
+
public boolean mayHaveArithmeticOrLogicalBinop() {
- // TODO(b/7145202413): Implement this.
- return true;
+ // ArithmeticBinop
+ assert Opcodes.ADD < 64;
+ assert Opcodes.DIV < 64;
+ assert Opcodes.MUL < 64;
+ assert Opcodes.REM < 64;
+ assert Opcodes.SUB < 64;
+ // LogicalBinop
+ assert Opcodes.AND < 64;
+ assert Opcodes.OR < 64;
+ assert Opcodes.SHL < 64;
+ assert Opcodes.SHR < 64;
+ assert Opcodes.USHR >= 64;
+ assert Opcodes.XOR >= 64;
+ long mask =
+ (1L << Opcodes.ADD)
+ | (1L << Opcodes.DIV)
+ | (1L << Opcodes.MUL)
+ | (1L << Opcodes.REM)
+ | (1L << Opcodes.SUB)
+ | (1L << Opcodes.AND)
+ | (1L << Opcodes.OR)
+ | (1L << Opcodes.SHL)
+ | (1L << Opcodes.SHR)
+ | (1L << Opcodes.USHR)
+ | (1L << Opcodes.XOR);
+ long other = (1L << (Opcodes.USHR - 64)) | (1L << (Opcodes.XOR - 64));
+ boolean result = isAnySetInFirst(mask) || isAnySetInSecond(other);
+ assert result
+ == (mayHaveAdd()
+ || mayHaveDiv()
+ || mayHaveMul()
+ || mayHaveRem()
+ || mayHaveSub()
+ || mayHaveAnd()
+ || mayHaveOr()
+ || mayHaveShl()
+ || mayHaveShr()
+ || mayHaveUshr()
+ || mayHaveXor());
+ return result;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 6893464..8deda3f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -268,6 +268,7 @@
/**
* Returns the basic block containing this instruction.
*/
+ @Override
public BasicBlock getBlock() {
assert block != null;
return block;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index f4ff24a..315568e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
@@ -57,6 +58,15 @@
Value insertConstIntInstruction(IRCode code, InternalOptions options, int value);
+ void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value);
+
+ void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues);
+
/**
* Replace the current instruction with null throwing instructions.
*
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java b/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java
index e9a5347..144bbdb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java
@@ -21,4 +21,6 @@
default Phi asPhi() {
return null;
}
+
+ BasicBlock getBlock();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index aea5a70..87b93a4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -17,6 +16,7 @@
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.List;
public abstract class InvokeMethodWithReceiver extends InvokeMethod {
@@ -74,15 +74,15 @@
TypeLatticeElement receiverType = receiver.getTypeLattice();
assert receiverType.isPreciseType();
- if (appView.appInfo().hasSubtyping()) {
- AppView<? extends AppInfoWithSubtyping> appViewWithSubtyping = appView.withSubtyping();
+ if (appView.appInfo().hasLiveness()) {
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
ClassTypeLatticeElement receiverLowerBoundType =
- receiver.getDynamicLowerBoundType(appViewWithSubtyping);
+ receiver.getDynamicLowerBoundType(appViewWithLiveness);
if (receiverLowerBoundType != null) {
DexType refinedReceiverType =
- TypeAnalysis.getRefinedReceiverType(appViewWithSubtyping, this);
+ TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this);
assert receiverLowerBoundType.getClassType() == refinedReceiverType
- || receiverLowerBoundType.isBasedOnMissingClass(appViewWithSubtyping);
+ || receiverLowerBoundType.isBasedOnMissingClass(appViewWithLiveness);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index 2c76690..3a098ca 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ListIterator;
@@ -50,6 +51,22 @@
}
@Override
+ public void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value) {
+ currentBlockIterator.replaceCurrentInstructionWithConstInt(appView, code, value);
+ }
+
+ @Override
+ public void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues) {
+ currentBlockIterator.replaceCurrentInstructionWithStaticGet(
+ appView, code, field, affectedValues);
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithSubtyping> appView,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 3252d67..2f0911d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -67,6 +67,7 @@
return this;
}
+ @Override
public BasicBlock getBlock() {
return block;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index f9ca31d..dca6174 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -12,13 +14,13 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
-import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.regalloc.LiveIntervals;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SetUtils;
@@ -1114,21 +1116,22 @@
return lattice;
}
- public ClassTypeLatticeElement getDynamicLowerBoundType(
- AppView<? extends AppInfoWithSubtyping> appView) {
+ public ClassTypeLatticeElement getDynamicLowerBoundType(AppView<AppInfoWithLiveness> appView) {
Value root = getAliasedValue();
if (root.isPhi()) {
return null;
}
+
Instruction definition = root.definition;
if (definition.isNewInstance()) {
DexType type = definition.asNewInstance().clazz;
DexClass clazz = appView.definitionFor(type);
if (clazz != null && !clazz.isInterface()) {
- return ClassTypeLatticeElement.create(type, Nullability.definitelyNotNull(), appView);
+ return ClassTypeLatticeElement.create(type, definitelyNotNull(), appView);
}
return null;
}
+
// Try to find an alias of the receiver, which is defined by an instruction of the type
// Assume<DynamicTypeAssumption>.
Value aliasedValue = getSpecificAliasedValue(value -> value.definition.isAssumeDynamicType());
@@ -1136,9 +1139,20 @@
ClassTypeLatticeElement lattice =
aliasedValue.definition.asAssumeDynamicType().getAssumption().getDynamicLowerBoundType();
return lattice != null && typeLattice.isDefinitelyNotNull() && lattice.isNullable()
- ? lattice.asMeetWithNotNull().asClassTypeLatticeElement()
+ ? lattice.asMeetWithNotNull()
: lattice;
}
+
+ // If it is a final or effectively-final class type, then we know the lower bound.
+ if (getTypeLattice().isClassType()) {
+ ClassTypeLatticeElement classType = getTypeLattice().asClassTypeLatticeElement();
+ DexType type = classType.getClassType();
+ DexClass clazz = appView.definitionFor(type);
+ if (clazz != null && clazz.isEffectivelyFinal(appView)) {
+ return classType;
+ }
+ }
+
return null;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index fd1b111..3e9ecab 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -73,6 +73,7 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
+import com.android.tools.r8.ir.optimize.library.LibraryMethodOptimizer;
import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer;
import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer;
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
@@ -126,6 +127,7 @@
private final Outliner outliner;
private final ClassInitializerDefaultsOptimization classInitializerDefaultsOptimization;
private final FieldBitAccessAnalysis fieldBitAccessAnalysis;
+ private final LibraryMethodOptimizer libraryMethodOptimizer;
private final LibraryMethodOverrideAnalysis libraryMethodOverrideAnalysis;
private final StringConcatRewriter stringConcatRewriter;
private final StringOptimizer stringOptimizer;
@@ -225,6 +227,7 @@
this.classInliner = null;
this.classStaticizer = null;
this.fieldBitAccessAnalysis = null;
+ this.libraryMethodOptimizer = null;
this.libraryMethodOverrideAnalysis = null;
this.inliner = null;
this.outliner = null;
@@ -286,6 +289,7 @@
options.enableFieldBitAccessAnalysis
? new FieldBitAccessAnalysis(appViewWithLiveness)
: null;
+ this.libraryMethodOptimizer = new LibraryMethodOptimizer(appViewWithLiveness);
this.libraryMethodOverrideAnalysis =
options.enableTreeShakingOfLibraryMethodOverrides
? new LibraryMethodOverrideAnalysis(appViewWithLiveness)
@@ -322,6 +326,7 @@
this.classStaticizer = null;
this.dynamicTypeOptimization = null;
this.fieldBitAccessAnalysis = null;
+ this.libraryMethodOptimizer = null;
this.libraryMethodOverrideAnalysis = null;
this.inliner = null;
this.lambdaMerger = null;
@@ -1185,6 +1190,9 @@
// Reflection/string optimization 3. trivial conversion/computation on const-string
stringOptimizer.computeTrivialOperationsOnConstString(code);
stringOptimizer.removeTrivialConversions(code);
+ if (libraryMethodOptimizer != null) {
+ libraryMethodOptimizer.optimize(appView, code, feedback, methodProcessor);
+ }
assert code.isConsistentSSA();
}
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 d48d1f2..3a78425 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
@@ -51,6 +51,7 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InstructionOrPhi;
import com.android.tools.r8.ir.code.IntSwitch;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.InvokeDirect;
@@ -1646,8 +1647,6 @@
return;
}
- code.numberInstructionsPerBlock();
-
for (BasicBlock block : code.blocks) {
InstructionListIterator instructionIterator = block.listIterator(code);
// Collect all the non constant in values for binop/lit8 or binop/lit16 instructions.
@@ -1669,26 +1668,32 @@
Reference2IntMap<Value> lastUseOfBinopsWithLit8OrLit16NonConstantValues =
new Reference2IntOpenHashMap<>();
lastUseOfBinopsWithLit8OrLit16NonConstantValues.defaultReturnValue(-1);
+ int currentInstructionNumber = block.getInstructions().size();
while (instructionIterator.hasPrevious()) {
Instruction currentInstruction = instructionIterator.previous();
- for (Value value : Iterables.concat(currentInstruction.inValues(), currentInstruction.getDebugValues())) {
+ currentInstructionNumber--;
+ for (Value value :
+ Iterables.concat(currentInstruction.inValues(), currentInstruction.getDebugValues())) {
if (!binopsWithLit8OrLit16NonConstantValues.contains(value)) {
continue;
}
if (!lastUseOfBinopsWithLit8OrLit16NonConstantValues.containsKey(value)) {
- lastUseOfBinopsWithLit8OrLit16NonConstantValues.put(
- value, currentInstruction.getNumber());
+ lastUseOfBinopsWithLit8OrLit16NonConstantValues.put(value, currentInstructionNumber);
}
}
}
// Do the transformation except if the binop can use the binop/2addr format.
+ currentInstructionNumber--;
+ assert currentInstructionNumber == -1;
while (instructionIterator.hasNext()) {
Instruction currentInstruction = instructionIterator.next();
+ currentInstructionNumber++;
if (!isBinopWithLit8OrLit16(currentInstruction)) {
continue;
}
Binop binop = currentInstruction.asBinop();
- if (!canBe2AddrInstruction(binop, lastUseOfBinopsWithLit8OrLit16NonConstantValues)) {
+ if (!canBe2AddrInstruction(
+ binop, currentInstructionNumber, lastUseOfBinopsWithLit8OrLit16NonConstantValues)) {
Value constValue = binopWithLit8OrLit16Constant(currentInstruction);
if (constValue.numberOfAllUsers() > 1) {
// No need to do the transformation if the const value is already used only one time.
@@ -1706,9 +1711,6 @@
}
}
- code.clearInstructionNumbers();
- assert code.hasNoInstructionNumbers();
-
assert code.isConsistentSSA();
}
@@ -1751,55 +1753,46 @@
}
/**
- * Estimate if a binary operation can be a 2addr form or not. It can be a 2addr form when an
+ * Estimate if a binary operation can be a binop/2addr form or not. It can be a 2addr form when an
* argument is no longer needed after the binary operation and can be overwritten. That is
* definitely the case if there is no path between the binary operation and all other usages.
*/
private static boolean canBe2AddrInstruction(
- Binop binop, Reference2IntMap<Value> lastUseOfRelevantValue) {
+ Binop binop, int binopInstructionNumber, Reference2IntMap<Value> lastUseOfRelevantValue) {
Value value = binopWithLit8OrLit16NonConstant(binop);
assert value != null;
- int number = lastUseOfRelevantValue.getInt(value);
- if (number > 0 && number > binop.getNumber()) {
+ int lastUseInstructionNumber = lastUseOfRelevantValue.getInt(value);
+ // The binop instruction is a user, so there is always a last use in the block.
+ assert lastUseInstructionNumber != -1;
+ if (lastUseInstructionNumber > binopInstructionNumber) {
return false;
}
- Iterable<Instruction> users =
+
+ Set<BasicBlock> noPathTo = Sets.newIdentityHashSet();
+ BasicBlock binopBlock = binop.getBlock();
+ Iterable<InstructionOrPhi> users =
value.debugUsers() != null
- ? Iterables.concat(value.uniqueUsers(), value.debugUsers())
- : value.uniqueUsers();
-
- for (Instruction user : users) {
- if (hasPath(binop, user)) {
+ ? Iterables.concat(value.uniqueUsers(), value.debugUsers(), value.uniquePhiUsers())
+ : Iterables.concat(value.uniqueUsers(), value.uniquePhiUsers());
+ for (InstructionOrPhi user : users) {
+ BasicBlock userBlock = user.getBlock();
+ if (userBlock == binopBlock) {
+ // All users in the current block are either before the binop instruction or the
+ // binop instruction itself.
+ continue;
+ }
+ if (noPathTo.contains(userBlock)) {
+ continue;
+ }
+ if (binopBlock.hasPathTo(userBlock)) {
return false;
}
- }
-
- for (Phi user : value.uniquePhiUsers()) {
- if (binop.getBlock().hasPathTo(user.getBlock())) {
- return false;
- }
+ noPathTo.add(userBlock);
}
return true;
}
- /**
- * Return true if there is a path between {@code source} instruction and {@code target}
- * instruction.
- */
- private static boolean hasPath(Instruction source, Instruction target) {
- BasicBlock sourceBlock = source.getBlock();
- BasicBlock targetBlock = target.getBlock();
- if (sourceBlock == targetBlock) {
- // Instructions must be numbered when getting here.
- assert source.getNumber() != -1;
- assert target.getNumber() != -1;
- return source.getNumber() < target.getNumber();
- }
-
- return source.getBlock().hasPathTo(targetBlock);
- }
-
public void shortenLiveRanges(IRCode code) {
// Currently, we are only shortening the live range of ConstNumbers in the entry block
// and ConstStrings with one user.
@@ -3019,7 +3012,7 @@
newInstruction.setBlock(phiBlock);
// The xor is replacing a phi so it does not have an actual position.
newInstruction.setPosition(phiPosition);
- phiBlock.getInstructions().add(insertIndex, newInstruction);
+ phiBlock.listIterator(code, insertIndex).add(newInstruction);
deadPhis++;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index d9e51ea..06c5dfe 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -76,7 +76,8 @@
MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
if (optimizationInfo.returnsArgument()) {
- assert optimizationInfo.getDynamicLowerBoundType() == null;
+ // Don't insert an assume-instruction since we will replace all usages of the out-value by
+ // the corresponding argument.
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 0195c84..303b4ea 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -154,7 +154,7 @@
}
}
}
- // TODO(b/119596718): Use dominant tree to extend it to non-canonicalized in values?
+ // TODO(b/145259212): Use dominant tree to extend it to non-canonicalized in values?
// For now, interested in inputs that are also canonicalized constants.
boolean invocationCanBeMovedToEntryBlock = true;
for (Value in : current.inValues()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 36112c9..0ba6b03 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -944,8 +944,8 @@
return false;
}
- // TODO(b/124842076) Extend this check to use checksNullReceiverBeforeAnySideEffect.
- if (receiver.getTypeLattice().isNullable()) {
+ // A definitely null receiver will throw an error on call site.
+ if (receiver.getTypeLattice().nullability().isDefinitelyNull()) {
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
index ae5e066..041616c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
@@ -13,9 +13,9 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Lists;
import com.google.common.io.BaseEncoding;
@@ -163,22 +163,26 @@
protected abstract String getGroupSuffix();
- final DexProgramClass synthesizeClass(InternalOptions options) {
+ final DexProgramClass synthesizeClass(
+ AppView<? extends AppInfoWithSubtyping> appView, OptimizationFeedback feedback) {
assert classType == null;
assert verifyLambdaIds(true);
List<LambdaInfo> lambdas = Lists.newArrayList(this.lambdas.values());
classType =
- options.itemFactory.createType(
- "L"
- + getTypePackage()
- + "-$$LambdaGroup$"
- + getGroupSuffix()
- + createHash(lambdas)
- + ";");
- return getBuilder(options.itemFactory).synthesizeClass(options);
+ appView
+ .dexItemFactory()
+ .createType(
+ "L"
+ + getTypePackage()
+ + "-$$LambdaGroup$"
+ + getGroupSuffix()
+ + createHash(lambdas)
+ + ";");
+ return getBuilder(appView.dexItemFactory()).synthesizeClass(appView, feedback);
}
- protected abstract LambdaGroupClassBuilder getBuilder(DexItemFactory factory);
+ protected abstract LambdaGroupClassBuilder<? extends LambdaGroup> getBuilder(
+ DexItemFactory factory);
private String createHash(List<LambdaInfo> lambdas) {
try {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
index e569714..2c4315c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.lambda;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
@@ -14,8 +16,8 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.InternalOptions;
import java.util.Collections;
import java.util.List;
@@ -32,7 +34,8 @@
this.origin = origin;
}
- public final DexProgramClass synthesizeClass(InternalOptions options) {
+ public final DexProgramClass synthesizeClass(
+ AppView<? extends AppInfoWithSubtyping> appView, OptimizationFeedback feedback) {
DexType groupClassType = group.getGroupClassType();
DexType superClassType = getSuperClassType();
DexProgramClass programClass =
@@ -49,7 +52,7 @@
buildEnclosingMethodAttribute(),
buildInnerClasses(),
buildAnnotations(),
- buildStaticFields(),
+ buildStaticFields(appView, feedback),
buildInstanceFields(),
buildDirectMethods(),
buildVirtualMethods(),
@@ -75,7 +78,8 @@
protected abstract DexEncodedField[] buildInstanceFields();
- protected abstract DexEncodedField[] buildStaticFields();
+ protected abstract DexEncodedField[] buildStaticFields(
+ AppView<? extends AppInfoWithSubtyping> appView, OptimizationFeedback feedback);
protected abstract DexTypeList buildInterfaces();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 3bb09ce..9981290 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -326,7 +326,7 @@
// Remove invalidated lambdas, compact groups to ensure
// sequential lambda ids, create group lambda classes.
- BiMap<LambdaGroup, DexProgramClass> lambdaGroupsClasses = finalizeLambdaGroups();
+ BiMap<LambdaGroup, DexProgramClass> lambdaGroupsClasses = finalizeLambdaGroups(feedback);
// Fixup optimization info to ensure that the optimization info does not refer to any merged
// lambdas.
@@ -388,7 +388,7 @@
ThreadUtils.awaitFutures(futures);
}
- private BiMap<LambdaGroup, DexProgramClass> finalizeLambdaGroups() {
+ private BiMap<LambdaGroup, DexProgramClass> finalizeLambdaGroups(OptimizationFeedback feedback) {
for (DexType lambda : invalidatedLambdas) {
LambdaGroup group = lambdas.get(lambda);
assert group != null;
@@ -405,7 +405,7 @@
for (LambdaGroup group : groups.values()) {
assert !group.isTrivial() : "No trivial group is expected here.";
group.compact();
- DexProgramClass lambdaGroupClass = group.synthesizeClass(appView.options());
+ DexProgramClass lambdaGroupClass = group.synthesizeClass(appView, feedback);
result.put(group, lambdaGroupClass);
}
return result;
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 db9f04e..07c1b08 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
@@ -19,7 +19,6 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder;
import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -107,7 +106,7 @@
}
@Override
- protected LambdaGroupClassBuilder getBuilder(DexItemFactory factory) {
+ protected ClassBuilder getBuilder(DexItemFactory factory) {
return new ClassBuilder(factory, "java-style lambda group");
}
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 1595e8e..9e07c44 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
@@ -22,7 +22,6 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder;
import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -113,7 +112,7 @@
}
@Override
- protected LambdaGroupClassBuilder getBuilder(DexItemFactory factory) {
+ protected ClassBuilder getBuilder(DexItemFactory factory) {
return new ClassBuilder(factory, "kotlin-style lambda group");
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index 15d4160..74a5236 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -4,11 +4,16 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
@@ -20,7 +25,9 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
@@ -212,18 +219,30 @@
}
@Override
- protected DexEncodedField[] buildStaticFields() {
+ protected DexEncodedField[] buildStaticFields(
+ AppView<? extends AppInfoWithSubtyping> appView, OptimizationFeedback feedback) {
if (!group.isStateless()) {
return DexEncodedField.EMPTY_ARRAY;
}
// One field for each singleton lambda in the group.
List<DexEncodedField> result = new ArrayList<>(group.size());
- group.forEachLambda(info -> {
- if (group.isSingletonLambda(info.clazz.type)) {
- result.add(new DexEncodedField(group.getSingletonInstanceField(factory, info.id),
- SINGLETON_FIELD_FLAGS, DexAnnotationSet.empty(), DexValueNull.NULL));
- }
- });
+ group.forEachLambda(
+ info -> {
+ if (group.isSingletonLambda(info.clazz.type)) {
+ DexField field = group.getSingletonInstanceField(factory, info.id);
+ DexEncodedField encodedField =
+ new DexEncodedField(
+ field, SINGLETON_FIELD_FLAGS, DexAnnotationSet.empty(), DexValueNull.NULL);
+ result.add(encodedField);
+
+ // Record that the field is definitely not null. It is guaranteed to be assigned in the
+ // class initializer of the enclosing class before it is read.
+ ClassTypeLatticeElement exactType =
+ ClassTypeLatticeElement.create(field.type, definitelyNotNull(), appView);
+ feedback.markFieldHasDynamicLowerBoundType(encodedField, exactType);
+ feedback.markFieldHasDynamicUpperBoundType(encodedField, exactType);
+ }
+ });
assert result.isEmpty() == !group.hasAnySingletons();
return result.toArray(DexEncodedField.EMPTY_ARRAY);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
new file mode 100644
index 0000000..edd657b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2019, 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.optimize.library;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+public class BooleanMethodOptimizer implements LibraryMethodModelCollection {
+
+ private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final DexItemFactory dexItemFactory;
+
+ BooleanMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ @Override
+ public DexType getType() {
+ return dexItemFactory.boxedBooleanType;
+ }
+
+ @Override
+ public void optimize(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues) {
+ if (singleTarget.method == dexItemFactory.booleanMembers.booleanValue) {
+ optimizeBooleanValue(code, instructionIterator, invoke);
+ } else if (singleTarget.method == dexItemFactory.booleanMembers.valueOf) {
+ optimizeValueOf(code, instructionIterator, invoke, affectedValues);
+ }
+ }
+
+ private void optimizeBooleanValue(
+ IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
+ Value argument = invoke.arguments().get(0).getAliasedValue();
+ if (!argument.isPhi()) {
+ Instruction definition = argument.definition;
+ if (definition.isStaticGet()) {
+ StaticGet staticGet = definition.asStaticGet();
+ DexField field = staticGet.getField();
+ if (field == dexItemFactory.booleanMembers.TRUE) {
+ instructionIterator.replaceCurrentInstructionWithConstInt(appView, code, 1);
+ } else if (field == dexItemFactory.booleanMembers.FALSE) {
+ instructionIterator.replaceCurrentInstructionWithConstInt(appView, code, 0);
+ }
+ }
+ }
+ }
+
+ private void optimizeValueOf(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ Set<Value> affectedValues) {
+ Value argument = invoke.arguments().get(0);
+ AbstractValue abstractValue = argument.getAbstractValue(appView, code.method.method.holder);
+ if (abstractValue.isSingleNumberValue()) {
+ instructionIterator.replaceCurrentInstructionWithStaticGet(
+ appView,
+ code,
+ abstractValue.asSingleNumberValue().getBooleanValue()
+ ? dexItemFactory.booleanMembers.TRUE
+ : dexItemFactory.booleanMembers.FALSE,
+ affectedValues);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
new file mode 100644
index 0000000..5608b94
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2019, 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.optimize.library;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+/** Used to model the behavior of library methods for optimization purposes. */
+public interface LibraryMethodModelCollection {
+
+ /**
+ * The library class whose methods are being modeled by this collection of models. As an example,
+ * {@link BooleanMethodOptimizer} is modeling {@link Boolean}).
+ */
+ DexType getType();
+
+ /**
+ * Invoked for instructions in {@param code} that invoke a method on the class returned by {@link
+ * #getType()}. The given {@param singleTarget} is guaranteed to be non-null.
+ */
+ void optimize(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
new file mode 100644
index 0000000..0a92679
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2019, 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.optimize.library;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.CodeOptimization;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class LibraryMethodOptimizer implements CodeOptimization {
+
+ private final Map<DexType, LibraryMethodModelCollection> libraryMethodModelCollections =
+ new IdentityHashMap<>();
+
+ public LibraryMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
+ register(new BooleanMethodOptimizer(appView));
+ }
+
+ private void register(LibraryMethodModelCollection optimizer) {
+ LibraryMethodModelCollection existing =
+ libraryMethodModelCollections.put(optimizer.getType(), optimizer);
+ assert existing == null;
+ }
+
+ @Override
+ public void optimize(
+ AppView<?> appView,
+ IRCode code,
+ OptimizationFeedback feedback,
+ MethodProcessor methodProcessor) {
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
+ InstructionListIterator instructionIterator = code.instructionListIterator();
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ if (instruction.isInvokeMethod()) {
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ DexEncodedMethod singleTarget =
+ invoke.lookupSingleTarget(appView, code.method.method.holder);
+ if (singleTarget != null) {
+ optimizeInvoke(code, instructionIterator, invoke, singleTarget, affectedValues);
+ }
+ }
+ }
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+ }
+
+ private void optimizeInvoke(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues) {
+ LibraryMethodModelCollection optimizer =
+ libraryMethodModelCollections.getOrDefault(
+ singleTarget.method.holder, NopLibraryMethodModelCollection.getInstance());
+ optimizer.optimize(code, instructionIterator, invoke, singleTarget, affectedValues);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java
new file mode 100644
index 0000000..9a0980f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2019, 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.optimize.library;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+public class NopLibraryMethodModelCollection implements LibraryMethodModelCollection {
+
+ private static final NopLibraryMethodModelCollection INSTANCE =
+ new NopLibraryMethodModelCollection();
+
+ private NopLibraryMethodModelCollection() {}
+
+ public static NopLibraryMethodModelCollection getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public DexType getType() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void optimize(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues) {}
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index a467cb2..12992ca 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -94,7 +94,9 @@
.add(this::insertAssumeInstructions)
.add(collectOptimizationInfo(feedback)));
- // Enqueue instance methods to be staticized (only remove references to 'this').
+ // Enqueue instance methods to be staticized (only remove references to 'this'). Intentionally
+ // not collection optimization info for these methods, since they will be reprocessed again
+ // below once staticized.
enqueueMethodsWithCodeOptimizations(
methodsToBeStaticized, optimizations -> optimizations.add(this::removeReferencesToThis));
diff --git a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
index 612be2c6..88f89c3 100644
--- a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -20,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -118,6 +120,8 @@
updateConstraint(inliningConstraints.forConstClass(type, invocationContext));
} else if (cst instanceof Handle) {
updateConstraint(inliningConstraints.forConstMethodHandle());
+ } else if (cst instanceof ConstantDynamic) {
+ throw new CompilationError("Unsupported dynamic constant: " + cst.toString());
} else {
updateConstraint(inliningConstraints.forConstInstruction());
}
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
index 991b861..fc9ea8b 100644
--- a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
@@ -15,6 +16,7 @@
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
+import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -60,6 +62,8 @@
} else {
registry.registerConstClass(application.getType((Type) cst));
}
+ } else if (cst instanceof ConstantDynamic) {
+ throw new CompilationError("Unsupported dynamic constant: " + cst.toString());
} else if (cst instanceof Handle) {
DexMethodHandle handle = DexMethodHandle.fromAsmHandle((Handle) cst, application, clazz);
registry.registerMethodHandle(
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index c008660..6347853 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -23,23 +23,20 @@
// Enum describing the possible/supported CF runtimes.
public enum CfVm {
- JDK8("jdk8"),
- JDK9("jdk9"),
- JDK11("jdk11");
+ JDK8("jdk8", 52),
+ JDK9("jdk9", 53),
+ JDK11("jdk11", 55);
private final String name;
+ private final int classfileVersion;
- public static CfVm fromName(String v) {
- for (CfVm value : CfVm.values()) {
- if (value.name.equals(v)) {
- return value;
- }
- }
- return null;
+ CfVm(String name, int classfileVersion) {
+ this.name = name;
+ this.classfileVersion = classfileVersion;
}
- CfVm(String name) {
- this.name = name;
+ public int getClassfileVersion() {
+ return classfileVersion;
}
public static CfVm first() {
@@ -58,10 +55,6 @@
return this.ordinal() <= other.ordinal();
}
- public boolean hasModularRuntime() {
- return this != JDK8;
- }
-
@Override
public String toString() {
return name;
diff --git a/src/test/java/com/android/tools/r8/VersionTests.java b/src/test/java/com/android/tools/r8/VersionTests.java
index ba5ee78..dffaa2b 100644
--- a/src/test/java/com/android/tools/r8/VersionTests.java
+++ b/src/test/java/com/android/tools/r8/VersionTests.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.Version.LABEL;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -62,4 +63,30 @@
public void testDevelopmentPredicate() {
assertEquals(LABEL.equals("master") || LABEL.contains("-dev"), Version.isDevelopmentVersion());
}
+
+ @Test
+ public void testLabelParsing() {
+ assertEquals(-1, Version.getMajorVersion("master"));
+ assertEquals(-1, Version.getMinorVersion("master"));
+ assertEquals(-1, Version.getPatchVersion("master"));
+ assertNull(Version.getPreReleaseString("master"));
+ // 'master' is checked before 'isEngineering'.
+ assertTrue(Version.isDevelopmentVersion("master", false));
+ assertTrue(Version.isDevelopmentVersion("master", true));
+
+ assertEquals(1, Version.getMajorVersion("1.2.3-dev"));
+ assertEquals(2, Version.getMinorVersion("1.2.3-dev"));
+ assertEquals(3, Version.getPatchVersion("1.2.3-dev"));
+ assertEquals("dev", Version.getPreReleaseString("1.2.3-dev"));
+ // '-dev' suffix is checked before 'isEngineering'.
+ assertTrue(Version.isDevelopmentVersion("1.2.3-dev", false));
+ assertTrue(Version.isDevelopmentVersion("1.2.3-dev", true));
+
+ assertEquals(1, Version.getMajorVersion("1.2.3"));
+ assertEquals(2, Version.getMinorVersion("1.2.3"));
+ assertEquals(3, Version.getPatchVersion("1.2.3"));
+ assertEquals("", Version.getPreReleaseString("1.2.3"));
+ assertFalse(Version.isDevelopmentVersion("1.2.3", false));
+ assertTrue(Version.isDevelopmentVersion("1.2.3", true));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java b/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java
new file mode 100644
index 0000000..81f50fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, 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.cf;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ConstantDynamic;
+import org.objectweb.asm.Handle;
+
+@RunWith(Parameterized.class)
+public class ConstantDynamicHolderTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ final TestParameters parameters;
+
+ public ConstantDynamicHolderTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClassFileData(getTransformedMain())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("null");
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addProgramClassFileData(getTransformedMain())
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorMessageThatMatches(
+ containsString("Unsupported dynamic constant")));
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getTransformedMain())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorMessageThatMatches(
+ containsString("Unsupported dynamic constant")));
+ }
+
+ private byte[] getTransformedMain() throws IOException {
+ return transformer(Main.class)
+ .setMinVersion(CfVm.JDK11)
+ .transformLdcInsnInMethod(
+ "main",
+ (value, continuation) -> {
+ assertEquals("replaced by dynamic null constant", value);
+ continuation.apply(getDynamicConstant());
+ })
+ .transform();
+ }
+
+ private ConstantDynamic getDynamicConstant() {
+ return new ConstantDynamic(
+ "dynamicnull",
+ "Ljava/lang/String;",
+ new Handle(
+ H_INVOKESTATIC,
+ "java/lang/invoke/ConstantBootstraps",
+ "nullConstant",
+ "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)"
+ + "Ljava/lang/Object;",
+ false));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("replaced by dynamic null constant");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
index 62e1bf9..874d4ad 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
@@ -48,7 +48,8 @@
return buildParameters(
BooleanUtils.values(),
getTestParameters()
- .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+ // TODO(b/145281519): Should be Version.V5_1_1.
+ .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withAllApiLevels()
.build());
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java
index c1b808b..d3a87fa 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java
@@ -68,12 +68,14 @@
{
Map<String, Boolean> map = new HashMap<>();
// After canonicalization, only one 'true' and one 'false' conversions remain.
- map.put("A", true);
- map.put("B", true);
- map.put("C", false);
- map.put("D", true);
- map.put("E", false);
- map.put("F", true);
+ boolean alwaysTrue = System.currentTimeMillis() >= 0;
+ boolean alwaysFalse = System.currentTimeMillis() < 0;
+ map.put("A", alwaysTrue);
+ map.put("B", alwaysTrue);
+ map.put("C", alwaysFalse);
+ map.put("D", alwaysTrue);
+ map.put("E", alwaysFalse);
+ map.put("F", alwaysTrue);
System.out.println(map.get("B"));
System.out.println(map.get("E"));
}
@@ -207,9 +209,12 @@
test(
result,
TOTAL_MAX_CALLS,
- EXPECTED_BOOLEAN_VALUE_OF,
- EXPECTED_INTEGER_VALUE_OF,
- EXPECTED_LONG_VALUE_OF);
+ // TODO(b/145259212): Should be `EXPECTED_BOOLEAN_VALUE_OF` (2).
+ 6,
+ // TODO(b/145253152): Should be `EXPECTED_INTEGER_VALUE_OF` (2).
+ 5,
+ // TODO(b/145253152): Should be `EXPECTED_LONG_VALUE_OF` (7).
+ 10);
result =
testForD8()
@@ -221,9 +226,12 @@
test(
result,
TOTAL_MAX_CALLS,
- EXPECTED_BOOLEAN_VALUE_OF,
- EXPECTED_INTEGER_VALUE_OF,
- EXPECTED_LONG_VALUE_OF);
+ // TODO(b/145259212): Should be `EXPECTED_BOOLEAN_VALUE_O` (2).
+ 6,
+ // TODO(b/145253152): Should be `EXPECTED_INTEGER_VALUE_OF` (2).
+ 5,
+ // TODO(b/145253152): Should be `EXPECTED_LONG_VALUE_OF` (7).
+ 10);
}
@Test
@@ -238,9 +246,12 @@
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
int expectedMaxCount = parameters.isCfRuntime() ? TOTAL_MAX_CALLS : EXPECTED_MAX_CALLS;
- int expectedBooleanValueOfCount = parameters.isCfRuntime() ? 6 : EXPECTED_BOOLEAN_VALUE_OF;
- int expectedIntValueOfCount = parameters.isCfRuntime() ? 5 : EXPECTED_INTEGER_VALUE_OF;
- int expectedLongValueOfCount = parameters.isCfRuntime() ? 10 : EXPECTED_LONG_VALUE_OF;
+ // TODO(b/145259212): Should be `EXPECTED_BOOLEAN_VALUE_OF` (2) when compiling for dex.
+ int expectedBooleanValueOfCount = 6;
+ // TODO(b/145253152): Should be `EXPECTED_INTEGER_VALUE_OF` (2) when compiling for dex.
+ int expectedIntValueOfCount = 5;
+ // TODO(b/145253152): Should be `EXPECTED_LONG_VALUE_OF` (7) when compiling for dex.
+ int expectedLongValueOfCount = 10;
test(
result,
expectedMaxCount,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/BooleanValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/BooleanValueOfTest.java
new file mode 100644
index 0000000..85eb82b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/BooleanValueOfTest.java
@@ -0,0 +1,179 @@
+// Copyright (c) 2019, 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.optimize.library;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.google.common.base.Predicates.or;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class BooleanValueOfTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public BooleanValueOfTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(BooleanValueOfTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("true", "false", "true", "false", "true", "false");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+
+ MethodSubject testBooleanValueOfTrue =
+ testClassSubject.uniqueMethodWithName("testBooleanValueOfTrue");
+ assertThat(testBooleanValueOfTrue, isPresent());
+ assertTrue(
+ testBooleanValueOfTrue
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch("booleanValue"::equals));
+ assertTrue(
+ testBooleanValueOfTrue
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(staticGet -> staticGet.getField().name.toSourceString())
+ .noneMatch("TRUE"::equals));
+
+ MethodSubject testBooleanValueOfFalse =
+ testClassSubject.uniqueMethodWithName("testBooleanValueOfFalse");
+ assertThat(testBooleanValueOfFalse, isPresent());
+ assertTrue(
+ testBooleanValueOfFalse
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch("booleanValue"::equals));
+ assertTrue(
+ testBooleanValueOfFalse
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(staticGet -> staticGet.getField().name.toSourceString())
+ .noneMatch("FALSE"::equals));
+
+ MethodSubject testRoundTripTrueMethodSubject =
+ testClassSubject.uniqueMethodWithName("testRoundTripTrue");
+ assertThat(testRoundTripTrueMethodSubject, isPresent());
+ assertTrue(
+ testRoundTripTrueMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch(or("booleanValue"::equals, "valueOf"::equals)));
+
+ MethodSubject testRoundTripFalseMethodSubject =
+ testClassSubject.uniqueMethodWithName("testRoundTripFalse");
+ assertThat(testRoundTripFalseMethodSubject, isPresent());
+ assertTrue(
+ testRoundTripFalseMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch(or("booleanValue"::equals, "valueOf"::equals)));
+
+ MethodSubject testValueOfTrue = testClassSubject.uniqueMethodWithName("testValueOfTrue");
+ assertThat(testValueOfTrue, isPresent());
+ assertTrue(
+ testValueOfTrue
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch("valueOf"::equals));
+ assertTrue(
+ testValueOfTrue
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(staticGet -> staticGet.getField().name.toSourceString())
+ .anyMatch("TRUE"::equals));
+
+ MethodSubject testValueOfFalse = testClassSubject.uniqueMethodWithName("testValueOfFalse");
+ assertThat(testValueOfFalse, isPresent());
+ assertTrue(
+ testValueOfFalse
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().name.toSourceString())
+ .noneMatch("valueOf"::equals));
+ assertTrue(
+ testValueOfFalse
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(staticGet -> staticGet.getField().name.toSourceString())
+ .anyMatch("FALSE"::equals));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ testBooleanValueOfTrue();
+ testBooleanValueOfFalse();
+ testRoundTripTrue();
+ testRoundTripFalse();
+ testValueOfTrue();
+ testValueOfFalse();
+ }
+
+ @NeverInline
+ static void testBooleanValueOfTrue() {
+ System.out.println(Boolean.TRUE.booleanValue());
+ }
+
+ @NeverInline
+ static void testBooleanValueOfFalse() {
+ System.out.println(Boolean.FALSE.booleanValue());
+ }
+
+ @NeverInline
+ static void testRoundTripTrue() {
+ System.out.println(Boolean.valueOf(true).booleanValue());
+ }
+
+ @NeverInline
+ static void testRoundTripFalse() {
+ System.out.println(Boolean.valueOf(false).booleanValue());
+ }
+
+ @NeverInline
+ static void testValueOfTrue() {
+ System.out.println(Boolean.valueOf(true));
+ }
+
+ @NeverInline
+ static void testValueOfFalse() {
+ System.out.println(Boolean.valueOf(false));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index e83a89f..555b94c 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -58,6 +59,21 @@
}
@Override
+ public void replaceCurrentInstructionWithConstInt(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, int value) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public void replaceCurrentInstructionWithStaticGet(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ IRCode code,
+ DexField field,
+ Set<Value> affectedValues) {
+ throw new Unimplemented();
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithSubtyping> appView,
IRCode code,
diff --git a/src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
similarity index 79%
copy from src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java
copy to src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
index 786177c..63c6161 100644
--- a/src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
@@ -1,7 +1,7 @@
// Copyright (c) 2019, 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.graph.access;
+package com.android.tools.r8.resolution.access;
import static com.android.tools.r8.TestRuntime.CfVm.JDK11;
import static org.hamcrest.core.StringContains.containsString;
@@ -27,8 +27,9 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class NestMethodAccessTest extends TestBase {
- static final String EXPECTED = StringUtils.lines("A::bar", "A::baz");
+public class NestStaticMethodAccessTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("A::bar");
private final TestParameters parameters;
private final boolean inSameNest;
@@ -44,7 +45,7 @@
BooleanUtils.values());
}
- public NestMethodAccessTest(TestParameters parameters, boolean inSameNest) {
+ public NestStaticMethodAccessTest(TestParameters parameters, boolean inSameNest) {
this.parameters = parameters;
this.inSameNest = inSameNest;
}
@@ -55,10 +56,7 @@
public Collection<byte[]> getTransformedClasses() throws Exception {
return ImmutableList.of(
- withNest(A.class)
- .setPrivate(A.class.getDeclaredMethod("bar"))
- .setPrivate(A.class.getDeclaredMethod("baz"))
- .transform(),
+ withNest(A.class).setPrivate(A.class.getDeclaredMethod("bar")).transform(),
withNest(B.class).transform());
}
@@ -96,17 +94,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .disassemble()
- .apply(
- result -> {
- if (parameters.isDexRuntime() && inSameNest) {
- // TODO(b/145187969): R8 incorrectly compiles the nest based access away.
- result.assertFailureWithErrorThatMatches(
- containsString(NullPointerException.class.getName()));
- } else {
- checkExpectedResult(result);
- }
- });
+ .apply(this::checkExpectedResult);
}
private void checkExpectedResult(TestRunResult<?> result) {
@@ -127,21 +115,15 @@
}
static class A {
- /* will be private */ void bar() {
+ /* will be private */ static void bar() {
System.out.println("A::bar");
}
-
- /* will be private */ static void baz() {
- System.out.println("A::baz");
- }
}
static class B {
public void foo() {
- // Virtual invoke to private method.
- new A().bar();
// Static invoke to private method.
- A.baz();
+ A.bar();
}
}
diff --git a/src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
similarity index 76%
copy from src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java
copy to src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
index 786177c..a487f40 100644
--- a/src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
@@ -1,7 +1,7 @@
// Copyright (c) 2019, 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.graph.access;
+package com.android.tools.r8.resolution.access;
import static com.android.tools.r8.TestRuntime.CfVm.JDK11;
import static org.hamcrest.core.StringContains.containsString;
@@ -27,8 +27,9 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class NestMethodAccessTest extends TestBase {
- static final String EXPECTED = StringUtils.lines("A::bar", "A::baz");
+public class NestStaticMethodAccessWithIntermediateClassTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("A::bar");
private final TestParameters parameters;
private final boolean inSameNest;
@@ -44,7 +45,8 @@
BooleanUtils.values());
}
- public NestMethodAccessTest(TestParameters parameters, boolean inSameNest) {
+ public NestStaticMethodAccessWithIntermediateClassTest(
+ TestParameters parameters, boolean inSameNest) {
this.parameters = parameters;
this.inSameNest = inSameNest;
}
@@ -55,11 +57,9 @@
public Collection<byte[]> getTransformedClasses() throws Exception {
return ImmutableList.of(
- withNest(A.class)
- .setPrivate(A.class.getDeclaredMethod("bar"))
- .setPrivate(A.class.getDeclaredMethod("baz"))
- .transform(),
- withNest(B.class).transform());
+ withNest(A.class).setPrivate(A.class.getDeclaredMethod("bar")).transform(),
+ withNest(B.class).transform(),
+ withNest(C.class).transform());
}
@Test
@@ -96,13 +96,11 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .disassemble()
.apply(
result -> {
- if (parameters.isDexRuntime() && inSameNest) {
- // TODO(b/145187969): R8 incorrectly compiles the nest based access away.
- result.assertFailureWithErrorThatMatches(
- containsString(NullPointerException.class.getName()));
+ if (inSameNest) {
+ // TODO(b/145187969): R8 incorrectly compiles out the incorrect access.
+ result.assertSuccessWithOutput(EXPECTED);
} else {
checkExpectedResult(result);
}
@@ -110,8 +108,9 @@
}
private void checkExpectedResult(TestRunResult<?> result) {
- if (inSameNest) {
- result.assertSuccessWithOutput(EXPECTED);
+ if (inSameNest && parameters.isCfRuntime()) {
+ // TODO(b/145187969): Investigate if the change to NoSuchMethodError is according to spec?
+ result.assertFailureWithErrorThatMatches(containsString(NoSuchMethodError.class.getName()));
} else {
result.assertFailureWithErrorThatMatches(containsString(IllegalAccessError.class.getName()));
}
@@ -119,35 +118,31 @@
private ClassFileTransformer withNest(Class<?> clazz) throws Exception {
if (inSameNest) {
- // If in the same nest make A host and B a member.
- return transformer(clazz).setNest(A.class, B.class);
+ // If in the same nest make A host with B and C as members.
+ return transformer(clazz).setNest(A.class, B.class, C.class);
}
// Otherwise, set the class to be its own host and no additional members.
return transformer(clazz).setNest(clazz);
}
static class A {
- /* will be private */ void bar() {
+ /* will be private */ static void bar() {
System.out.println("A::bar");
}
-
- /* will be private */ static void baz() {
- System.out.println("A::baz");
- }
}
- static class B {
+ static class B extends A {}
+
+ static class C {
public void foo() {
- // Virtual invoke to private method.
- new A().bar();
// Static invoke to private method.
- A.baz();
+ B.bar();
}
}
static class Main {
public static void main(String[] args) {
- new B().foo();
+ new C().foo();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
similarity index 90%
rename from src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java
rename to src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
index 786177c..59267b2 100644
--- a/src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
@@ -1,7 +1,7 @@
// Copyright (c) 2019, 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.graph.access;
+package com.android.tools.r8.resolution.access;
import static com.android.tools.r8.TestRuntime.CfVm.JDK11;
import static org.hamcrest.core.StringContains.containsString;
@@ -27,8 +27,9 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class NestMethodAccessTest extends TestBase {
- static final String EXPECTED = StringUtils.lines("A::bar", "A::baz");
+public class NestVirtualMethodAccessTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("A::bar");
private final TestParameters parameters;
private final boolean inSameNest;
@@ -44,7 +45,7 @@
BooleanUtils.values());
}
- public NestMethodAccessTest(TestParameters parameters, boolean inSameNest) {
+ public NestVirtualMethodAccessTest(TestParameters parameters, boolean inSameNest) {
this.parameters = parameters;
this.inSameNest = inSameNest;
}
@@ -57,7 +58,6 @@
return ImmutableList.of(
withNest(A.class)
.setPrivate(A.class.getDeclaredMethod("bar"))
- .setPrivate(A.class.getDeclaredMethod("baz"))
.transform(),
withNest(B.class).transform());
}
@@ -96,7 +96,6 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .disassemble()
.apply(
result -> {
if (parameters.isDexRuntime() && inSameNest) {
@@ -130,18 +129,12 @@
/* will be private */ void bar() {
System.out.println("A::bar");
}
-
- /* will be private */ static void baz() {
- System.out.println("A::baz");
- }
}
static class B {
public void foo() {
// Virtual invoke to private method.
new A().bar();
- // Static invoke to private method.
- A.baz();
}
}
diff --git a/src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
similarity index 67%
copy from src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java
copy to src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
index 786177c..68cc59e 100644
--- a/src/test/java/com/android/tools/r8/graph/access/NestMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
@@ -1,12 +1,15 @@
// Copyright (c) 2019, 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.graph.access;
+package com.android.tools.r8.resolution.access;
import static com.android.tools.r8.TestRuntime.CfVm.JDK11;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
@@ -27,8 +30,9 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class NestMethodAccessTest extends TestBase {
- static final String EXPECTED = StringUtils.lines("A::bar", "A::baz");
+public class NestVirtualMethodAccessWithIntermediateClassTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("A::bar");
private final TestParameters parameters;
private final boolean inSameNest;
@@ -44,7 +48,8 @@
BooleanUtils.values());
}
- public NestMethodAccessTest(TestParameters parameters, boolean inSameNest) {
+ public NestVirtualMethodAccessWithIntermediateClassTest(
+ TestParameters parameters, boolean inSameNest) {
this.parameters = parameters;
this.inSameNest = inSameNest;
}
@@ -55,11 +60,9 @@
public Collection<byte[]> getTransformedClasses() throws Exception {
return ImmutableList.of(
- withNest(A.class)
- .setPrivate(A.class.getDeclaredMethod("bar"))
- .setPrivate(A.class.getDeclaredMethod("baz"))
- .transform(),
- withNest(B.class).transform());
+ withNest(A.class).setPrivate(A.class.getDeclaredMethod("bar")).transform(),
+ withNest(B.class).transform(),
+ withNest(C.class).transform());
}
@Test
@@ -90,19 +93,31 @@
@Test
public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addProgramClasses(getClasses())
- .addProgramClassFileData(getTransformedClasses())
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Main.class)
+ R8FullTestBuilder builder =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(getClasses())
+ .addProgramClassFileData(getTransformedClasses())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class);
+ // TODO(b/145196085): R8 fails compilation when the classes are in the same nest.
+ if (inSameNest) {
+ if (parameters.isDexRuntime()) {
+ try {
+ builder.compile();
+ } catch (CompilationFailedException e) {
+ // Expected failure.
+ return;
+ }
+ fail("Expected failure: b/145196085");
+ }
+ }
+ builder
.run(parameters.getRuntime(), Main.class)
- .disassemble()
.apply(
result -> {
- if (parameters.isDexRuntime() && inSameNest) {
- // TODO(b/145187969): R8 incorrectly compiles the nest based access away.
- result.assertFailureWithErrorThatMatches(
- containsString(NullPointerException.class.getName()));
+ if (inSameNest) {
+ // TODO(b/145187969): R8 compiles out the errors when in the same nest.
+ result.assertSuccessWithOutput(EXPECTED);
} else {
checkExpectedResult(result);
}
@@ -110,8 +125,9 @@
}
private void checkExpectedResult(TestRunResult<?> result) {
- if (inSameNest) {
- result.assertSuccessWithOutput(EXPECTED);
+ if (inSameNest && parameters.isCfRuntime()) {
+ // TODO(b/145187969): Investigate if the change to NoSuchMethodError is according to spec?
+ result.assertFailureWithErrorThatMatches(containsString(NoSuchMethodError.class.getName()));
} else {
result.assertFailureWithErrorThatMatches(containsString(IllegalAccessError.class.getName()));
}
@@ -119,8 +135,8 @@
private ClassFileTransformer withNest(Class<?> clazz) throws Exception {
if (inSameNest) {
- // If in the same nest make A host and B a member.
- return transformer(clazz).setNest(A.class, B.class);
+ // If in the same nest make A host with B and C as members.
+ return transformer(clazz).setNest(A.class, B.class, C.class);
}
// Otherwise, set the class to be its own host and no additional members.
return transformer(clazz).setNest(clazz);
@@ -130,24 +146,20 @@
/* will be private */ void bar() {
System.out.println("A::bar");
}
-
- /* will be private */ static void baz() {
- System.out.println("A::baz");
- }
}
- static class B {
+ static class B extends A {}
+
+ static class C {
public void foo() {
// Virtual invoke to private method.
- new A().bar();
- // Static invoke to private method.
- A.baz();
+ new B().bar();
}
}
static class Main {
public static void main(String[] args) {
- new B().foo();
+ new C().foo();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index d0d40bb..09faf46 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -5,6 +5,7 @@
import static org.objectweb.asm.Opcodes.ASM7;
+import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.MethodAccessFlags;
@@ -27,8 +28,6 @@
public class ClassFileTransformer {
- private static final int NEST_SUPPORTED_VERSION = 55;
-
/**
* Basic algorithm for transforming the content of a class file.
*
@@ -166,6 +165,10 @@
});
}
+ public ClassFileTransformer setMinVersion(CfVm jdk) {
+ return setMinVersion(jdk.getClassfileVersion());
+ }
+
public ClassFileTransformer setMinVersion(int minVersion) {
return addClassTransformer(
new ClassTransformer() {
@@ -185,7 +188,7 @@
public ClassFileTransformer setNest(Class<?> host, Class<?>... members) {
assert !Arrays.asList(members).contains(host);
- return setMinVersion(NEST_SUPPORTED_VERSION)
+ return setMinVersion(CfVm.JDK11)
.addClassTransformer(
new ClassTransformer() {
@@ -294,4 +297,31 @@
}
});
}
+
+ /** Abstraction of the MethodVisitor.visitLdcInsn method with its continuation. */
+ @FunctionalInterface
+ public interface LdcInsnTransform {
+ void visitLdcInsn(Object value, LdcInsnTransformContinuation continuation);
+ }
+
+ /** Continuation for transforming a method. Will continue with the super visitor if called. */
+ @FunctionalInterface
+ public interface LdcInsnTransformContinuation {
+ void apply(Object value);
+ }
+
+ public ClassFileTransformer transformLdcInsnInMethod(
+ String methodName, LdcInsnTransform transform) {
+ return addMethodTransformer(
+ new MethodTransformer() {
+ @Override
+ public void visitLdcInsn(Object value) {
+ if (getContext().method.getMethodName().equals(methodName)) {
+ transform.visitLdcInsn(value, super::visitLdcInsn);
+ } else {
+ super.visitLdcInsn(value);
+ }
+ }
+ });
+ }
}
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index 0cac61a..d059fb3 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -60,11 +60,13 @@
for i in range(len(srcs)):
src = os.path.join(root, srcs[i])
dest = os.path.join(target_root, 'prebuilts', 'r8', dests[i])
- print 'Copying: ' + src + ' -> ' + dest
- copyfile(src, dest)
- if maps:
- print 'Copying: ' + src + '.map -> ' + dest + '.map'
- copyfile(src + '.map', dest + '.map')
+ if os.path.exists(dest):
+ print 'Copying: ' + src + ' -> ' + dest
+ copyfile(src, dest)
+ if maps:
+ print 'Copying: ' + src + '.map -> ' + dest + '.map'
+ copyfile(src + '.map', dest + '.map')
+
def copy_jar_targets(root, target_root, jar_targets, maps):
srcs = map((lambda t: t[0] + '.jar'), jar_targets)