Merge "Add reproduction test for b/120164595"
diff --git a/build.gradle b/build.gradle
index 977ebd7..d2df8f8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -257,6 +257,7 @@
def r8LibPath = "$buildDir/libs/r8lib.jar"
def r8LibGeneratedKeepRulesPath = "$buildDir/generated/keep.txt"
def r8LibTestPath = "$buildDir/classes/r8libtest"
+def r8TestsJarPath = "$buildDir/libs/r8tests.jar"
def osString = OperatingSystem.current().isLinux() ? "linux" :
OperatingSystem.current().isMacOsX() ? "mac" : "windows"
@@ -565,9 +566,7 @@
}
from repackageSources.outputs.files
from repackageDeps.outputs.files
- doLast {
- configureRelocations(it)
- }
+ configureRelocations(it)
}
task r8WithoutDeps(type: ShadowJar) {
@@ -599,6 +598,14 @@
}
}
+task R8NoManifestNoDeps(type: ShadowJar) {
+ from consolidatedLicense.outputs.files
+ baseName 'r8nomanifest-exclude-deps'
+ classifier = null
+ version = null
+ from sourceSets.main.output
+}
+
task R8NoManifest(type: ShadowJar) {
from consolidatedLicense.outputs.files
baseName 'r8nomanifest'
@@ -639,7 +646,8 @@
}
def baseR8CommandLine(args = []) {
- return ["java", "-ea", "-jar", R8.outputs.files[0]] + args
+ // Execute r8 commands against a stable r8 with relocated dependencies.
+ return ["java", "-ea", "-jar", r8WithRelocatedDeps.outputs.files[0]] + args
}
def r8CfCommandLine(input, output, pgconf, args = [], libs = []) {
@@ -655,34 +663,42 @@
def r8LibCreateTask(name, pgConf, r8Task, output, args = [], libs = []) {
return tasks.create("r8Lib${name}", Exec) {
- inputs.files ([pgConf, R8.outputs, r8Task.outputs])
+ inputs.files ([pgConf, r8WithRelocatedDeps.outputs, r8Task.outputs])
outputs.file output
dependsOn downloadOpenJDKrt
+ dependsOn r8WithRelocatedDeps
+ dependsOn r8Task
commandLine r8CfCommandLine(r8Task.outputs.files[0], output, pgConf, args, libs)
workingDir = projectDir
}
}
-task testJar(type: Jar, dependsOn: testClasses) {
+task testJar(type: ShadowJar, dependsOn: testClasses) {
+ outputs.upToDateWhen { false }
baseName = "r8tests"
from sourceSets.test.output
+ if (!project.hasProperty('exclude_deps')) {
+ relocate('com.google.common', 'com.android.tools.r8.com.google.common')
+ relocate('org.objectweb.asm', 'com.android.tools.r8.org.objectweb.asm')
+ }
}
task generateR8LibKeepRules(type: Exec) {
doFirst {
standardOutput new FileOutputStream(r8LibGeneratedKeepRulesPath)
}
- dependsOn R8
- dependsOn r8WithoutDeps
+ def libSourceTask = project.hasProperty('exclude_deps') ? R8NoManifestNoDeps : R8NoManifest
+ dependsOn r8WithRelocatedDeps
+ dependsOn libSourceTask
dependsOn testJar
dependsOn downloadOpenJDKrt
- inputs.files ([R8.outputs, r8WithoutDeps.outputs, testJar.outputs])
+ inputs.files ([r8WithRelocatedDeps.outputs, libSourceTask.outputs, testJar.outputs])
outputs.file r8LibGeneratedKeepRulesPath
commandLine baseR8CommandLine([
"printuses",
"--keeprules",
"third_party/openjdk/openjdk-rt-1.8/rt.jar",
- r8WithoutDeps.outputs.files[0],
+ libSourceTask.outputs.files[0],
testJar.outputs.files[0]])
workingDir = projectDir
}
@@ -691,15 +707,22 @@
dependsOn r8LibCreateTask(
"NoDeps",
"src/main/keep.txt",
- r8WithoutDeps,
+ R8NoManifestNoDeps,
r8LibPath,
["--pg-conf", generateR8LibKeepRules.outputs.files[0]],
repackageDepsNoRelocate.outputs.files
- ).dependsOn(repackageDepsNoRelocate, r8WithoutDeps, generateR8LibKeepRules)
+ ).dependsOn(repackageDepsNoRelocate, generateR8LibKeepRules)
+ outputs.file r8LibPath
}
task R8Lib {
- dependsOn r8LibCreateTask("Main", "src/main/keep.txt", R8NoManifest, r8LibPath)
+ dependsOn r8LibCreateTask(
+ "Main",
+ "src/main/keep.txt",
+ R8NoManifest,
+ r8LibPath,
+ ["--pg-conf", generateR8LibKeepRules.outputs.files[0]]
+ ).dependsOn(generateR8LibKeepRules)
outputs.file r8LibPath
}
@@ -1385,19 +1408,15 @@
}
task configureTestForR8Lib(type: Copy) {
- dependsOn testClasses
- // Setting classpath triggers a scan for test files in $buildDir/classes/test that finds all
- // tests and not just the ones under $test/com/android/tools/r8. That is generally not
- // something we want so we just copy the desired test files to $r8LibTestPath.
- // Note, we cannot use sourceSets.test.output here since it will copy all tests.
- def sourcePath = "$buildDir/classes/test/com/android/tools/r8"
- inputs.file sourcePath
+ dependsOn testJar
+ inputs.file r8TestsJarPath
if (getR8LibTask() != null) {
dependsOn getR8LibTask()
- def destPath = r8LibTestPath + "/com/android/tools/r8"
- delete destPath
- from(sourcePath)
- into(destPath)
+ delete r8LibTestPath
+ from zipTree(testJar.outputs.files[0])
+ into r8LibTestPath
+ include "com/android/tools/r8/**"
+ include "dalvik/**"
}
outputs.dir r8LibTestPath
}
@@ -1499,7 +1518,12 @@
}
if (project.hasProperty('r8lib') || project.hasProperty('r8lib_no_deps')) {
dependsOn configureTestForR8Lib
- classpath = files([r8LibPath]) + (sourceSets.test.runtimeClasspath - sourceSets.main.output)
+ // We remove build/classes/test from classpath and rely on configureTestForR8Lib to provide
+ // all needed tests in r8LibTestPath.
+ classpath = files([r8LibPath, r8LibTestPath]) +
+ sourceSets.test.runtimeClasspath -
+ sourceSets.main.output -
+ files(['build/classes/test'])
testClassesDir = new File(r8LibTestPath)
}
if (OperatingSystem.current().isLinux()
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
index ad8edd3..14aad66 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Value;
/**
@@ -346,24 +345,6 @@
return appInfo.dexItemFactory.createReferenceTypeLatticeElement(type, isNullable, appInfo);
}
- public static TypeLatticeElement fromNumericType(NumericType type) {
- switch (type) {
- case BYTE:
- case CHAR:
- case SHORT:
- case INT:
- return INT;
- case LONG:
- return LONG;
- case FLOAT:
- return FLOAT;
- case DOUBLE:
- return DOUBLE;
- default:
- throw new Unreachable("Unexpected numeric type: " + type);
- }
- }
-
public boolean isValueTypeCompatible(TypeLatticeElement other) {
return (isReference() && other.isReference())
|| (isSingle() && other.isSingle())
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 7dbb1f2..af2dae6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -29,17 +29,17 @@
protected Instruction current;
protected Position position = null;
- protected BasicBlockInstructionIterator(BasicBlock block) {
+ BasicBlockInstructionIterator(BasicBlock block) {
this.block = block;
this.listIterator = block.getInstructions().listIterator();
}
- protected BasicBlockInstructionIterator(BasicBlock block, int index) {
+ BasicBlockInstructionIterator(BasicBlock block, int index) {
this.block = block;
this.listIterator = block.getInstructions().listIterator(index);
}
- protected BasicBlockInstructionIterator(BasicBlock block, Instruction instruction) {
+ BasicBlockInstructionIterator(BasicBlock block, Instruction instruction) {
this(block);
nextUntil((x) -> x == instruction);
}
@@ -134,6 +134,8 @@
Value value = current.inValues().get(i);
value.removeUser(current);
}
+ // These needs to stay to ensure that an optimization incorrectly not taking debug info into
+ // account still produces valid code when run without enabled assertions.
for (Value value : current.getDebugValues()) {
value.removeDebugUser(current);
}
@@ -302,11 +304,8 @@
blocksIterator.previous();
}
assert IteratorUtils.peekNext(blocksIterator) == inlinee.blocks.getFirst();
-
// Iterate through the inlined blocks.
- Iterator<BasicBlock> inlinedBlocksIterator = inlinee.blocks.iterator();
- while (inlinedBlocksIterator.hasNext()) {
- BasicBlock inlinedBlock = inlinedBlocksIterator.next();
+ for (BasicBlock inlinedBlock : inlinee.blocks) {
BasicBlock expected = blocksIterator.next();
assert inlinedBlock == expected; // Iterators must be in sync.
if (inlinedBlock.hasCatchHandlers()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
index 931a584..54567b5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Binop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
@@ -135,7 +136,7 @@
@Override
public TypeLatticeElement evaluate(AppInfo appInfo) {
- return TypeLatticeElement.fromNumericType(type);
+ return PrimitiveTypeLatticeElement.fromNumericType(type);
}
@Override
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 48b6c9b..a5e5d78 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
@@ -9,7 +9,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
-import java.util.function.Predicate;
public interface InstructionListIterator
extends ListIterator<Instruction>,
@@ -46,23 +45,6 @@
return next;
}
- /**
- * Continue to call {@link #next} while {@code predicate} tests {@code false}.
- *
- * @return the instruction that matched the predicate or {@code null} if all instructions fails
- * the predicate test
- */
- @Override
- default Instruction nextUntil(Predicate<Instruction> predicate) {
- while (hasNext()) {
- Instruction instruction = next();
- if (predicate.test(instruction)) {
- return instruction;
- }
- }
- return null;
- }
-
default void setInsertionPosition(Position position) {
// Intentionally empty.
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 8f32081..6e5e5d7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -155,12 +155,14 @@
LoadStoreHelper loadStoreHelper = new LoadStoreHelper(code, typeVerificationHelper, appInfo);
loadStoreHelper.insertLoadsAndStores();
// Run optimizations on phis and basic blocks in a fixpoint.
- PhiOptimizations phiOptimizations = new PhiOptimizations();
- boolean reachedFixpoint = false;
- phiOptimizations.optimize(code);
- while (!reachedFixpoint) {
- BasicBlockMuncher.optimize(code);
- reachedFixpoint = !phiOptimizations.optimize(code);
+ if (!options.testing.disallowLoadStoreOptimization) {
+ PhiOptimizations phiOptimizations = new PhiOptimizations();
+ boolean reachedFixpoint = false;
+ phiOptimizations.optimize(code);
+ while (!reachedFixpoint) {
+ BasicBlockMuncher.optimize(code);
+ reachedFixpoint = !phiOptimizations.optimize(code);
+ }
}
registerAllocator = new CfRegisterAllocator(code, options, typeVerificationHelper);
registerAllocator.allocateRegisters();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index e08e9c4..41faa04 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -95,15 +95,11 @@
private static ConstNumber createConstNumberReplacement(
IRCode code, long constant, TypeLatticeElement typeLattice, DebugLocalInfo debugLocalInfo) {
- ConstNumber replacement;
- if (typeLattice.isReference()) {
- assert constant == 0;
- replacement = code.createConstNull();
- } else {
- Value returnedValue = code.createValue(typeLattice, debugLocalInfo);
- replacement = new ConstNumber(returnedValue, constant);
- }
- return replacement;
+ assert !typeLattice.isReference() || constant == 0;
+ Value returnedValue =
+ code.createValue(
+ typeLattice.isReference() ? TypeLatticeElement.NULL : typeLattice, debugLocalInfo);
+ return new ConstNumber(returnedValue, constant);
}
private void setValueRangeFromProguardRule(ProguardMemberRule rule, Value value) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index f9740c6..7966d3a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.Add;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -273,7 +274,7 @@
inValues.add(
builder.readRegister(register, ValueTypeConstraint.fromNumericType(numericType)));
}
- TypeLatticeElement latticeElement = TypeLatticeElement.fromNumericType(numericType);
+ TypeLatticeElement latticeElement = PrimitiveTypeLatticeElement.fromNumericType(numericType);
Value outValue =
builder.writeRegister(outline.argumentCount(), latticeElement, ThrowingInfo.CAN_THROW);
Instruction newInstruction = null;
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 243a0ee..c0c6e59 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -529,6 +529,7 @@
public boolean allowFailureOnInnerClassErrors = false;
public boolean noLocalsTableOnInput = false;
public boolean forceNameReflectionOptimization = false;
+ public boolean disallowLoadStoreOptimization = false;
}
private boolean hasMinApi(AndroidApiLevel level) {
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 3b5ddd8..e9d85be 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -76,6 +76,7 @@
Collections.addAll(
command,
getJavaExecutable(),
+ "-ea",
"-cp",
r8jar.toAbsolutePath().toString(),
R8.class.getTypeName(),
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTest.java b/src/test/java/com/android/tools/r8/cf/TryRangeTest.java
new file mode 100644
index 0000000..08e01ec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTest.java
@@ -0,0 +1,41 @@
+// 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 com.android.tools.r8.NeverInline;
+
+public class TryRangeTest {
+
+ @NeverInline
+ public static float doSomething(int x) throws Exception {
+ if (x == 42) {
+ throw new Exception("is 42");
+ } else {
+ return 1;
+ }
+ }
+
+ @NeverInline
+ public static void test(int count) {
+ int x = count;
+ float y;
+ if (x == 7) {
+ try {
+ y = doSomething(x);
+ } catch (Exception e) {
+ System.out.println(x);
+ return;
+ }
+ } else {
+ System.out.println(x);
+ y = 7;
+ }
+ System.out.println(y);
+ }
+
+ public static void main(String[] args) {
+ test(10);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
new file mode 100644
index 0000000..98542ff
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
@@ -0,0 +1,33 @@
+// 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 com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import org.junit.Test;
+
+/**
+ * This tests that we produce valid code when having normal-flow with exceptional edges in blocks.
+ * We might perform optimizations that add operations (dup, swap, etc.) before and after
+ * instructions that lie on the boundary of the exception table that is generated for a basic block.
+ * If live-ranges are minimized this could produce VerifyErrors. TODO(b/119771771) Will fail if
+ * shorten live ranges without shorten exception table range.
+ */
+public class TryRangeTestRunner extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ testForR8(Backend.CF)
+ .addProgramClasses(TryRangeTest.class)
+ .addKeepMainRule(TryRangeTest.class)
+ .setMode(CompilationMode.RELEASE)
+ .minification(false)
+ .noTreeShaking()
+ .enableInliningAnnotations()
+ .addOptionsModification(o -> o.testing.disallowLoadStoreOptimization = true)
+ .run(TryRangeTest.class)
+ .assertSuccess();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java b/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
index 83dbd9e..04477bd 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.FrameInspector;
+import com.android.tools.r8.debug.DebugTestConfig.RuntimeKind;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringUtils;
@@ -288,8 +289,10 @@
String sig = reference.getMethodSignature();
List<Variable> variables = reference.getVisibleVariables();
int frameDepth = reference.getFrameDepth();
+ RuntimeKind referenceRuntime = reference.getConfig().getRuntimeKind();
for (int i = 1; i < states.size(); i++) {
DebuggeeState state = states.get(i);
+ RuntimeKind stateRuntime = state.getConfig().getRuntimeKind();
if (verifyFiles) {
assertEquals("source file mismatch", file, state.getSourceFile());
}
@@ -304,7 +307,11 @@
"method mismatch", method + sig, state.getMethodName() + state.getMethodSignature());
}
if (verifyVariables) {
- verifyVariablesEqual(variables, state.getVisibleVariables());
+ verifyVariablesEqual(
+ referenceRuntime,
+ reference.getVisibleVariables(),
+ stateRuntime,
+ state.getVisibleVariables());
}
if (verifyStack) {
assertEquals(frameDepth, state.getFrameDepth());
@@ -312,20 +319,33 @@
FrameInspector referenceInspector = reference.getFrame(j);
FrameInspector stateInspector = state.getFrame(j);
verifyVariablesEqual(
- referenceInspector.getVisibleVariables(), stateInspector.getVisibleVariables());
+ referenceRuntime,
+ referenceInspector.getVisibleVariables(),
+ stateRuntime,
+ stateInspector.getVisibleVariables());
}
}
}
}
- private static void verifyVariablesEqual(List<Variable> xs, List<Variable> ys) {
+ private static boolean shouldIgnoreVariable(Variable variable, RuntimeKind runtime) {
+ return runtime == RuntimeKind.DEX && variable.getName().isEmpty();
+ }
+
+ private static void verifyVariablesEqual(
+ RuntimeKind xRuntime, List<Variable> xs, RuntimeKind yRuntime, List<Variable> ys) {
Map<String, Variable> map = new HashMap<>(xs.size());
for (Variable x : xs) {
- map.put(x.getName(), x);
+ if (!shouldIgnoreVariable(x, xRuntime)) {
+ map.put(x.getName(), x);
+ }
}
List<Variable> unexpected = new ArrayList<>(ys.size());
List<Pair<Variable, Variable>> different = new ArrayList<>(Math.min(xs.size(), ys.size()));
for (Variable y : ys) {
+ if (shouldIgnoreVariable(y, yRuntime)) {
+ continue;
+ }
Variable x = map.remove(y.getName());
if (x == null) {
unexpected.add(y);
diff --git a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
index 099f55b..17d5077 100644
--- a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
@@ -81,8 +81,7 @@
@Test
public void testBridgeMethod() throws Exception {
- // TODO(b/79671093): D8 has local variables with empty names.
- testDebuggingJvmOnly("bridge", "BridgeMethod");
+ testDebugging("bridge", "BridgeMethod");
}
@Test
@@ -107,8 +106,7 @@
@Test
public void testFloatingPointValuedAnnotation() throws Exception {
- // D8 has no source file.
- testDebuggingJvmOnly("floating_point_annotations", "FloatingPointValuedAnnotationTest");
+ testDebugging("floating_point_annotations", "FloatingPointValuedAnnotationTest");
}
@Test
@@ -190,14 +188,14 @@
@Test
public void testSync() throws Exception {
- // D8 has two local variables with empty names.
+ // TODO(b/79671093): Line number mismatch in D8.
testDebuggingJvmOnly("sync", "Sync");
}
@Test
public void testThrowing() throws Exception {
- // TODO(b/79671093): We don't match JVM's behavior on this example.
- testDebuggingJvmOutputOnly("throwing", "Throwing");
+ // TODO(b/79671093): D8 has unexpected variables (this in throwing.c <init>).
+ testDebuggingJvmOnly("throwing", "Throwing");
}
@Test
@@ -205,11 +203,9 @@
testDebugging("trivial", "Trivial");
}
- @Ignore("TODO(mathiasr): InvalidDebugInfoException in CfSourceCode")
@Test
public void testTryCatch() throws Exception {
- // TODO(b/79671093): We don't match JVM's behavior on this example.
- testDebuggingJvmOutputOnly("trycatch", "TryCatch");
+ testDebugging("trycatch", "TryCatch");
}
@Test
@@ -259,8 +255,7 @@
@Test
public void testRegress62300145() throws Exception {
- // D8 has no source file.
- testDebuggingJvmOnly("regress_62300145", "Regress");
+ testDebugging("regress_62300145", "Regress");
}
@Test
@@ -281,21 +276,17 @@
@Test
public void testRegress70736958() throws Exception {
- // D8 has a local variable with empty name.
- testDebuggingJvmOnly("regress_70736958", "Test");
+ testDebugging("regress_70736958", "Test");
}
- @Ignore("TODO(mathiasr): Different behavior CfSourceCode vs JarSourceCode")
@Test
public void testRegress70737019() throws Exception {
- // TODO(b/79671093): We don't match JVM's behavior on this example.
- testDebuggingJvmOutputOnly("regress_70737019", "Test");
+ testDebugging("regress_70737019", "Test");
}
@Test
public void testRegress72361252() throws Exception {
- // D8 output has variable with empty name.
- testDebuggingJvmOnly("regress_72361252", "Test");
+ testDebugging("regress_72361252", "Test");
}
@Test
@@ -341,6 +332,8 @@
.add("R8/CfSourceCode", r8cf())
.add("R8/JarSourceCode", r8jar())
.add("D8", d8())
+ // When running on CF and DEX runtimes, filter down to states within the test package.
+ .setFilter(state -> state.getClassName().startsWith(pkg))
.compare();
}
diff --git a/tools/archive.py b/tools/archive.py
index 78a8189..ce04494 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -92,9 +92,10 @@
raise Exception('You are not a bot, don\'t archive builds')
# Generate an r8-ed build without dependencies.
- # Note: build_r8lib does a gradle-clean, this must be the first command.
- build_r8lib('r8nomanifest', True, True, utils.R8LIB_KEEP_RULES,
- utils.R8LIB_EXCLUDE_DEPS_JAR)
+ # The '-Pno_internal' flag is important because we generate the lib based on uses in tests.
+ gradle.RunGradleExcludeDeps([utils.R8LIB_NO_DEPS, '-Pno_internal'])
+ shutil.copyfile(utils.R8LIB_JAR, utils.R8LIB_EXCLUDE_DEPS_JAR)
+ shutil.copyfile(utils.R8LIB_JAR + '.map', utils.R8LIB_EXCLUDE_DEPS_JAR + '.map')
# Create maven release which uses a build that exclude dependencies.
create_maven_release.main(["--out", utils.LIBS])
@@ -106,6 +107,7 @@
# Ensure all archived artifacts has been built before archiving.
# The target tasks postfixed by 'lib' depend on the actual target task so
# building it invokes the original task first.
+ # The '-Pno_internal' flag is important because we generate the lib based on uses in tests.
gradle.RunGradle([
utils.R8,
utils.D8,
@@ -114,6 +116,7 @@
utils.R8LIB,
utils.COMPATDXLIB,
utils.COMPATPROGUARDLIB,
+ '-Pno_internal'
])
version = GetVersion()
is_master = IsMaster(version)
diff --git a/tools/as_utils.py b/tools/as_utils.py
index 1eaa206..7b373f4 100644
--- a/tools/as_utils.py
+++ b/tools/as_utils.py
@@ -8,7 +8,7 @@
import utils
-def add_r8_dependency(checkout_dir):
+def add_r8_dependency(checkout_dir, minified):
build_file = os.path.join(checkout_dir, 'build.gradle')
assert os.path.isfile(build_file), 'Expected a file to be present at {}'.format(build_file)
@@ -27,12 +27,21 @@
is_inside_dependencies = True
if is_inside_dependencies:
if utils.R8_JAR in stripped:
+ if minified:
+ # Skip line to avoid dependency on r8.jar
+ continue
+ added_r8_dependency = True
+ elif utils.R8LIB_JAR in stripped:
+ if not minified:
+ # Skip line to avoid dependency on r8lib.jar
+ continue
added_r8_dependency = True
elif 'com.android.tools.build:gradle:' in stripped:
gradle_version = stripped[stripped.rindex(':')+1:-1]
if not added_r8_dependency:
indent = ''.ljust(line.index('classpath'))
- f.write('{}classpath files(\'{}\')\n'.format(indent, utils.R8_JAR))
+ jar = utils.R8LIB_JAR if minified else utils.R8_JAR
+ f.write('{}classpath files(\'{}\')\n'.format(indent, jar))
added_r8_dependency = True
elif stripped == '}':
is_inside_dependencies = False
@@ -51,5 +60,5 @@
lines = f.readlines()
with open(build_file, 'w') as f:
for line in lines:
- if utils.R8_JAR not in line:
+ if (utils.R8_JAR not in line) and (utils.R8LIB_JAR not in line):
f.write(line)
diff --git a/tools/historic_memory_usage.py b/tools/historic_memory_usage.py
new file mode 100755
index 0000000..fec7b31
--- /dev/null
+++ b/tools/historic_memory_usage.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+# 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.
+
+# Convenience script for running run_on_app.py finding minimum memory need for
+# compiling a given app back in time. This utilizes the prebuilt r8 jars on
+# cloud storage.
+# The script find all commits that exists on cloud storage in the given range.
+# It will then run the oldest and newest such commit, and gradually fill in
+# the commits in between.
+
+import optparse
+import os
+import subprocess
+import sys
+import utils
+
+APPS = ['gmscore', 'nest', 'youtube', 'gmail', 'chrome']
+COMPILERS = ['d8', 'r8']
+
+def ParseOptions(argv):
+ result = optparse.OptionParser()
+ result.add_option('--compiler',
+ help='The compiler to use',
+ default='d8',
+ choices=COMPILERS)
+ result.add_option('--app',
+ help='What app to run on',
+ default='gmail',
+ choices=APPS)
+ result.add_option('--top',
+ default=utils.get_HEAD_sha1(),
+ help='The most recent commit to test')
+ result.add_option('--bottom',
+ help='The oldest commit to test')
+ result.add_option('--output',
+ default='build',
+ help='Directory where to output results')
+ return result.parse_args(argv)
+
+
+class GitCommit(object):
+ def __init__(self, git_hash, destination, timestamp):
+ self.git_hash = git_hash
+ self.destination = destination
+ self.timestamp = timestamp
+
+ def __str__(self):
+ return '%s : %s (%s)' % (self.git_hash, self.destination, self.timestamp)
+
+ def __repr__(self):
+ return self.__str__()
+
+def git_commit_from_hash(hash):
+ commit_timestamp = subprocess.check_output(['git', 'show', '--no-patch',
+ '--no-notes', '--pretty=\'%ct\'',
+ hash]).strip().strip('\'')
+ destination = 'gs://r8-releases/raw/master/%s/r8.jar' % hash
+ commit = GitCommit(hash, destination, commit_timestamp)
+ return commit
+
+def enumerate_git_commits(options):
+ top = options.top if options.top else utils.get_HEAD_sha1()
+ # TODO(ricow): if not set, search back 1000
+ if not options.bottom:
+ raise Exception('No bottom specified')
+ bottom = options.bottom
+ output = subprocess.check_output(['git', 'rev-list', '--first-parent', top])
+ found_bottom = False
+ commits = []
+ for c in output.splitlines():
+ commits.append(git_commit_from_hash(c.strip()))
+ if c.strip() == bottom:
+ found_bottom = True
+ break
+ if not found_bottom:
+ raise Exception('Bottom not found, did you not use a merge commit')
+ return commits
+
+def get_available_commits(commits):
+ available_commits = []
+ for commit in commits:
+ if utils.cloud_storage_exists(commit.destination):
+ available_commits.append(commit)
+ return available_commits
+
+def print_commits(commits):
+ for commit in commits:
+ print(commit)
+
+def permutate_range(start, end):
+ diff = end - start
+ assert diff >= 0
+ if diff == 1:
+ return [start, end]
+ if diff == 0:
+ return [start]
+ half = end - (diff / 2)
+ numbers = [half]
+ first_half = permutate_range(start, half - 1)
+ second_half = permutate_range(half + 1, end)
+ for index in range(len(first_half)):
+ numbers.append(first_half[index])
+ if index < len(second_half):
+ numbers.append(second_half[index])
+ return numbers
+
+def permutate(number_of_commits):
+ assert number_of_commits > 0
+ numbers = permutate_range(0, number_of_commits - 1)
+ assert all(n in numbers for n in range(number_of_commits))
+ return numbers
+
+def pull_r8_from_cloud(commit):
+ utils.download_file_from_cloud_storage(commit.destination, utils.R8_JAR)
+
+def run_on_app(options, commit):
+ app = options.app
+ compiler = options.compiler
+ cmd = ['tools/run_on_app.py', '--app', app, '--compiler', compiler,
+ '--find-min-xmx']
+ stdout = subprocess.check_output(cmd)
+ output_path = options.output or 'build'
+ time_commit = '%s_%s' % (commit.timestamp, commit.git_hash)
+ time_commit_path = os.path.join(output_path, time_commit)
+ if not os.path.exists(time_commit_path):
+ os.makedirs(time_commit_path)
+ stdout_path = os.path.join(time_commit_path, 'stdout')
+ with open(stdout_path, 'w') as f:
+ f.write(stdout)
+ print('Wrote stdout to: %s' % stdout_path)
+
+
+def benchmark(commits, options):
+ commit_permutations = permutate(len(commits))
+ for index in commit_permutations:
+ commit = commits[index]
+ pull_r8_from_cloud(commit)
+ print('Running for commit: %s' % commit.git_hash)
+ run_on_app(options, commit)
+
+def main(argv):
+ (options, args) = ParseOptions(argv)
+ if not options.app:
+ raise Exception('Please specify an app')
+ commits = enumerate_git_commits(options)
+ available_commits = get_available_commits(commits)
+ print('Running for:')
+ print_commits(available_commits)
+ print('')
+ benchmark(available_commits, options)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index cba1d6e..41d4578 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -220,7 +220,8 @@
extra_args = []
app_provided_pg_conf = False;
# todo(121018500): remove when memory is under control
- extra_args.append('-Xmx8G')
+ if not any('-Xmx' in arg for arg in extra_args):
+ extra_args.append('-Xmx8G')
if options.golem:
golem.link_third_party()
options.out = os.getcwd()
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index e2f70f0..8f37f43 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -14,7 +14,7 @@
import as_utils
-SHRINKERS = ['r8', 'r8full', 'proguard']
+SHRINKERS = ['r8', 'r8full', 'r8-minified', 'r8full-minified', 'proguard']
WORKING_DIR = utils.BUILD
if 'R8_BENCHMARK_DIR' in os.environ and os.path.isdir(os.environ['R8_BENCHMARK_DIR']):
@@ -107,6 +107,9 @@
script = os.path.join(utils.TOOLS_DIR, 'extractmarker.py')
return '~~R8' in subprocess.check_output(['python', script, apk]).strip()
+def IsMinifiedR8(shrinker):
+ return shrinker == 'r8-minified' or shrinker == 'r8full-minified'
+
def IsTrackedByGit(file):
return subprocess.check_output(['git', 'ls-files', file]).strip() != ''
@@ -163,11 +166,6 @@
with utils.ChangedWorkingDirectory(checkout_dir):
GitPull()
- if options.use_tot:
- as_utils.add_r8_dependency(checkout_dir)
- else:
- as_utils.remove_r8_dependency(checkout_dir)
-
result_per_shrinker = {}
with utils.ChangedWorkingDirectory(checkout_dir):
@@ -203,6 +201,11 @@
def BuildAppWithShrinker(app, config, shrinker, checkout_dir, options):
print('Building {} with {}'.format(app, shrinker))
+ if options.disable_tot:
+ as_utils.remove_r8_dependency(checkout_dir)
+ else:
+ as_utils.add_r8_dependency(checkout_dir, IsMinifiedR8(shrinker))
+
app_module = config.get('app_module', 'app')
archives_base_name = config.get(' archives_base_name', app_module)
flavor = config.get('flavor')
@@ -214,7 +217,7 @@
with open("gradle.properties", "a") as gradle_properties:
if 'r8' in shrinker:
gradle_properties.write('\nandroid.enableR8=true\n')
- if shrinker == 'r8full':
+ if shrinker == 'r8full' or shrinker == 'r8full-minified':
gradle_properties.write('android.enableR8.fullMode=true\n')
else:
assert shrinker == 'proguard'
@@ -328,16 +331,26 @@
result.add_option('--shrinker',
help='The shrinker to use (by default, all are run)',
choices=SHRINKERS)
- result.add_option('--use_tot',
+ result.add_option('--disable_tot',
help='Whether to disable the use of the ToT version of R8',
- default=True,
- action='store_false')
+ default=False,
+ action='store_true')
return result.parse_args(argv)
def main(argv):
+ global SHRINKERS
+
(options, args) = ParseOptions(argv)
- assert not options.use_tot or os.path.isfile(utils.R8_JAR), (
+ assert options.disable_tot or os.path.isfile(utils.R8_JAR), (
'Cannot build from ToT without r8.jar')
+ assert options.disable_tot or os.path.isfile(utils.R8LIB_JAR), (
+ 'Cannot build from ToT without r8lib.jar')
+
+ if options.disable_tot:
+ # Cannot run r8 lib without adding r8lib.jar as an dependency
+ SHRINKERS = [
+ shrinker for shrinker in SHRINKERS
+ if 'minified' not in shrinker]
result_per_shrinker_per_app = {}
diff --git a/tools/test.py b/tools/test.py
index 043487c..6b24433 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -185,6 +185,7 @@
gradle_args.append('-Pr8lib')
if options.r8lib_no_deps:
gradle_args.append('-Pr8lib_no_deps')
+ gradle_args.append('-Pexclude_deps')
# Add Gradle tasks
gradle_args.append('cleanTest')
diff --git a/tools/utils.py b/tools/utils.py
index 9278459..e0ffa5c 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -30,6 +30,7 @@
D8 = 'd8'
R8 = 'r8'
R8LIB = 'r8lib'
+R8LIB_NO_DEPS = 'r8LibNoDeps'
R8_SRC = 'sourceJar'
COMPATDX = 'compatdx'
COMPATDXLIB = 'compatdxlib'