Merge "Fix the workaround for overlapping long registers art bug."
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 cb9fb20..dd29e8d2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -291,20 +291,24 @@
return canonicalize(fields, field);
}
- public DexProto createProto(DexString shorty, DexType type, DexTypeList parameters) {
+ public DexField createField(DexType clazz, DexType type, String name) {
+ return createField(clazz, type, createString(name));
+ }
+
+ public DexProto createProto(DexString shorty, DexType returnType, DexTypeList parameters) {
assert !sorted;
- DexProto proto = new DexProto(shorty, type, parameters);
+ DexProto proto = new DexProto(shorty, returnType, parameters);
return canonicalize(protos, proto);
}
- public DexProto createProto(DexString shorty, DexType type, DexType[] parameters) {
+ public DexProto createProto(DexString shorty, DexType returnType, DexType[] parameters) {
assert !sorted;
- return createProto(shorty, type,
+ return createProto(shorty, returnType,
parameters.length == 0 ? DexTypeList.empty() : new DexTypeList(parameters));
}
- public DexProto createProto(DexType type, DexType[] parameters) {
- return createProto(createShorty(type, parameters), type, parameters);
+ public DexProto createProto(DexType returnType, DexType[] parameters) {
+ return createProto(createShorty(returnType, parameters), returnType, parameters);
}
public DexString createShorty(DexType returnType, DexType[] argumentTypes) {
@@ -322,6 +326,10 @@
return canonicalize(methods, method);
}
+ public DexMethod createMethod(DexType holder, DexProto proto, String name) {
+ return createMethod(holder, proto, createString(name));
+ }
+
public DexMethodHandle createMethodHandle(
MethodHandleType type, Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod) {
assert !sorted;
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 232e0ff..8bb31d2 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
@@ -884,6 +884,21 @@
return block;
}
+ /**
+ * Create a new basic block with a single if instruction.
+ *
+ * <p>The constructed basic block has no predecessors and no successors.
+ *
+ * @param blockNumber the block number of the goto block
+ */
+ public static BasicBlock createIfBlock(int blockNumber, If theIf) {
+ BasicBlock block = new BasicBlock();
+ block.add(theIf);
+ block.close(null);
+ block.setNumber(blockNumber);
+ return block;
+ }
+
public boolean isTrivialGoto() {
return instructions.size() == 1 && exit().isGoto();
}
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 4aa06d6..8370eb8 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
@@ -472,6 +472,11 @@
codeRewriter.shortenLiveRanges(code);
codeRewriter.identifyReturnsArgument(method, code);
+ // Insert code to log arguments if requested.
+ if (options.methodMatchesLogArgumentsFilter(method)) {
+ codeRewriter.logArgumentTypes(method, code);
+ }
+
printMethod(code, "Optimized IR (SSA)");
// Perform register allocation.
RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
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 0830209..e5b8306 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
@@ -12,6 +12,7 @@
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;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.ArrayGet;
import com.android.tools.r8.ir.code.ArrayPut;
@@ -21,6 +22,7 @@
import com.android.tools.r8.ir.code.Cmp;
import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.ConstType;
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.Goto;
@@ -34,6 +36,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.JumpInstruction;
+import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.MoveType;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
@@ -1277,4 +1280,114 @@
}
return false;
}
+
+ private Value addConstString(IRCode code, InstructionListIterator iterator, String s) {
+ Value value = code.createValue(MoveType.OBJECT);;
+ iterator.add(new ConstString(value, dexItemFactory.createString(s)));
+ return value;
+ }
+
+ /**
+ * Insert code into <code>method</code> to log the argument types to System.out.
+ *
+ * The type is determined by calling getClass() on the argument.
+ */
+ public void logArgumentTypes(DexEncodedMethod method, IRCode code) {
+ List<Value> arguments = code.collectArguments();
+ BasicBlock block = code.blocks.getFirst();
+ InstructionListIterator iterator = block.listIterator();
+
+ // Split arguments into their own block.
+ iterator.nextUntil(instruction -> !instruction.isArgument());
+ iterator.previous();
+ iterator.split(code);
+ iterator.previous();
+
+ // Now that the block is split there should not be any catch handlers in the block.
+ assert !block.hasCatchHandlers();
+ Value out = code.createValue(MoveType.OBJECT);
+ DexType javaLangSystemType = dexItemFactory.createType("Ljava/lang/System;");
+ DexType javaIoPrintStreamType = dexItemFactory.createType("Ljava/io/PrintStream;");
+
+ DexProto proto = dexItemFactory.createProto(
+ dexItemFactory.voidType, new DexType[]{dexItemFactory.objectType});
+ DexMethod print = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "print");
+ DexMethod printLn = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "println");
+
+ iterator.add(
+ new StaticGet(MemberType.OBJECT, out,
+ dexItemFactory.createField(javaLangSystemType, javaIoPrintStreamType, "out")));
+
+ Value value = code.createValue(MoveType.OBJECT);
+ iterator.add(new ConstString(value, dexItemFactory.createString("INVOKE ")));
+ iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
+
+ value = code.createValue(MoveType.OBJECT);;
+ iterator.add(
+ new ConstString(value, dexItemFactory.createString(method.method.qualifiedName())));
+ iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
+
+ Value openParenthesis = addConstString(code, iterator, "(");
+ Value comma = addConstString(code, iterator, ",");
+ Value closeParenthesis = addConstString(code, iterator, ")");
+ Value indent = addConstString(code, iterator, " ");
+ Value nul = addConstString(code, iterator, "(null)");
+ Value primitive = addConstString(code, iterator, "(primitive)");
+ Value empty = addConstString(code, iterator, "");
+
+ iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, openParenthesis)));
+ for (int i = 0; i < arguments.size(); i++) {
+ iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, indent)));
+
+ // Add a block for end-of-line printing.
+ BasicBlock eol = BasicBlock.createGotoBlock(code.blocks.size());
+ code.blocks.add(eol);
+
+ BasicBlock successor = block.unlinkSingleSuccessor();
+ block.link(eol);
+ eol.link(successor);
+
+ Value argument = arguments.get(i);
+ if (argument.outType() != MoveType.OBJECT) {
+ iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, primitive)));
+ } else {
+ // Insert "if (argument != null) ...".
+ successor = block.unlinkSingleSuccessor();
+ If theIf = new If(If.Type.NE, argument);
+ BasicBlock ifBlock = BasicBlock.createIfBlock(code.blocks.size(), theIf);
+ code.blocks.add(ifBlock);
+ // Fallthrough block must be added right after the if.
+ BasicBlock isNullBlock = BasicBlock.createGotoBlock(code.blocks.size());
+ code.blocks.add(isNullBlock);
+ BasicBlock isNotNullBlock = BasicBlock.createGotoBlock(code.blocks.size());
+ code.blocks.add(isNotNullBlock);
+
+ // Link the added blocks together.
+ block.link(ifBlock);
+ ifBlock.link(isNotNullBlock);
+ ifBlock.link(isNullBlock);
+ isNotNullBlock.link(successor);
+ isNullBlock.link(successor);
+
+ // Fill code into the blocks.
+ iterator = isNullBlock.listIterator();
+ iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, nul)));
+ iterator = isNotNullBlock.listIterator();
+ value = code.createValue(MoveType.OBJECT);
+ iterator.add(new InvokeVirtual(dexItemFactory.objectMethods.getClass, value,
+ ImmutableList.of(arguments.get(i))));
+ iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
+ }
+
+ iterator = eol.listIterator();
+ if (i == arguments.size() - 1) {
+ iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, closeParenthesis)));
+ } else {
+ iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, comma)));
+ }
+ block = eol;
+ }
+ // When we fall out of the loop the iterator is in the last eol block.
+ iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, empty)));
+ }
}
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 3f8d992..5945741 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -51,6 +51,7 @@
public List<String> methodsFilter = ImmutableList.of();
public int minApiLevel = Constants.DEFAULT_ANDROID_API;
+ public List<String> logArgumentsFilter = ImmutableList.of();
// Defines interface method rewriter behavior.
public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Off;
@@ -110,6 +111,16 @@
return methodsFilter.indexOf(qualifiedName) >= 0;
}
+ public boolean methodMatchesLogArgumentsFilter(DexEncodedMethod method) {
+ // Not specifying a filter matches no methods.
+ if (logArgumentsFilter.size() == 0) {
+ return false;
+ }
+ // Currently the filter is simple string equality on the qualified name.
+ String qualifiedName = method.qualifiedName();
+ return logArgumentsFilter.indexOf(qualifiedName) >= 0;
+ }
+
public static class OutlineOptions {
public boolean enabled = true;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 333e77b..075c569 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -85,6 +85,17 @@
}
/**
+ * Compile an application with R8.
+ */
+ protected AndroidApp compileWithR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
+ throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+ R8Command command =
+ ToolHelper.prepareR8CommandBuilder(app)
+ .build();
+ return ToolHelper.runR8(command, optionsConsumer);
+ }
+
+ /**
* Compile an application with R8 using the supplied proguard configuration.
*/
protected AndroidApp compileWithR8(List<Class> classes, Path proguardConfig)
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 425dab7..d7fd468 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import java.io.IOException;
@@ -392,9 +393,16 @@
}
public static Path getClassFileForTestClass(Class clazz) {
- String[] parts = clazz.getCanonicalName().split("\\.");
- parts[parts.length - 1] = parts[parts.length - 1] + ".class";
- return getClassPathForTests().resolve(Paths.get("", parts));
+ List<String> parts = Lists.newArrayList(clazz.getCanonicalName().split("\\."));
+ Class enclosing = clazz;
+ while (enclosing.getEnclosingClass() != null) {
+ parts.set(parts.size() - 2, parts.get(parts.size() - 2) + "$" + parts.get(parts.size() - 1));
+ parts.remove(parts.size() - 1);
+ enclosing = clazz.getEnclosingClass();
+ }
+ parts.set(parts.size() - 1, parts.get(parts.size() - 1) + ".class");
+ return getClassPathForTests().resolve(
+ Paths.get("", parts.toArray(new String[parts.size() - 1])));
}
public static DexApplication buildApplication(List<String> fileNames)
diff --git a/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java b/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java
new file mode 100644
index 0000000..69db1ab
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2017, 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.rewrite.logarguments;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class LogArgumentsTest extends TestBase {
+
+ private int occourences(String match, String value) {
+ int count = 0;
+ int startIndex = 0;
+ while (true) {
+ int index = value.indexOf(match, startIndex);
+ if (index > 0) {
+ count++;
+ } else {
+ return count;
+ }
+ startIndex = index + match.length();
+ }
+ }
+
+ @Test
+ public void testStatic() throws Exception {
+ String qualifiedMethodName = "com.android.tools.r8.rewrite.logarguments.TestStatic.a";
+ AndroidApp app = compileWithR8(
+ readClasses(TestStatic.class),
+ options -> options.logArgumentsFilter = ImmutableList.of(qualifiedMethodName));
+ String result = runOnArt(app, TestStatic.class);
+ assertEquals(7, occourences(qualifiedMethodName, result));
+ assertEquals(3, occourences("(primitive)", result));
+ assertEquals(3, occourences("(null)", result));
+ assertEquals(1, occourences("java.lang.Object", result));
+ assertEquals(1, occourences("java.lang.Integer", result));
+ assertEquals(1, occourences("java.lang.String", result));
+ }
+
+ @Test
+ public void testInstance() throws Exception {
+ String qualifiedMethodName = "com.android.tools.r8.rewrite.logarguments.TestInstance.a";
+ AndroidApp app = compileWithR8(
+ readClasses(TestInstance.class),
+ options -> options.logArgumentsFilter = ImmutableList.of(qualifiedMethodName));
+ String result = runOnArt(app, TestInstance.class);
+ assertEquals(7, occourences(qualifiedMethodName, result));
+ assertEquals(7, occourences(
+ "class com.android.tools.r8.rewrite.logarguments.TestInstance", result));
+ assertEquals(3, occourences("(primitive)", result));
+ assertEquals(3, occourences("(null)", result));
+ assertEquals(1, occourences("java.lang.Object", result));
+ assertEquals(1, occourences("java.lang.Integer", result));
+ assertEquals(1, occourences("java.lang.String", result));
+ }
+
+ @Test
+ public void testInner() throws Exception {
+ String qualifiedMethodName = "com.android.tools.r8.rewrite.logarguments.TestInner$Inner.a";
+ AndroidApp app = compileWithR8(
+ readClasses(TestInner.class, TestInner.Inner.class),
+ options -> options.logArgumentsFilter = ImmutableList.of(qualifiedMethodName));
+ String result = runOnArt(app, TestInner.class);
+ assertEquals(7, occourences(qualifiedMethodName, result));
+ assertEquals(7, occourences(
+ "class com.android.tools.r8.rewrite.logarguments.TestInner$Inner", result));
+ assertEquals(3, occourences("(primitive)", result));
+ assertEquals(3, occourences("(null)", result));
+ assertEquals(1, occourences("java.lang.Object", result));
+ assertEquals(1, occourences("java.lang.Integer", result));
+ assertEquals(1, occourences("java.lang.String", result));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInner.java b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInner.java
new file mode 100644
index 0000000..ad4bcbc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInner.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2017, 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.rewrite.logarguments;
+
+public class TestInner {
+
+ public class Inner {
+
+ public int a() {
+ return 0;
+ }
+
+ public int a(int x) {
+ return x;
+ }
+
+ public int a(int x, int y) {
+ return x + y;
+ }
+
+ public Object a(Object o) {
+ return null;
+ }
+
+ public Object a(Object o1, Object o2) {
+ return null;
+ }
+ }
+
+ private Inner createInner() {
+ return new Inner();
+ }
+
+ public static void main(String[] args) {
+ Inner inner = new TestInner().createInner();
+ inner.a();
+ inner.a(1);
+ inner.a(1, 2);
+ inner.a(new Object());
+ inner.a(null);
+ inner.a(new Integer(0), "");
+ inner.a(null, null);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInstance.java b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInstance.java
new file mode 100644
index 0000000..c08aa12
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInstance.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2017, 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.rewrite.logarguments;
+
+public class TestInstance {
+
+ public int a() {
+ return 0;
+ }
+
+ public int a(int x) {
+ return x;
+ }
+
+ public int a(int x, int y) {
+ return x + y;
+ }
+
+ public Object a(Object o) {
+ return null;
+ }
+
+ public Object a(Object o1, Object o2) {
+ return null;
+ }
+
+ public static void main(String[] args) {
+ TestInstance test = new TestInstance();
+ test.a();
+ test.a(1);
+ test.a(1, 2);
+ test.a(new Object());
+ test.a(null);
+ test.a(new Integer(0), "");
+ test.a(null, null);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/logarguments/TestStatic.java b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestStatic.java
new file mode 100644
index 0000000..d65a2f0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestStatic.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2017, 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.rewrite.logarguments;
+
+public class TestStatic {
+
+ public static int a() {
+ return 0;
+ }
+
+ public static int a(int x) {
+ return x;
+ }
+
+ public static int a(int x, int y) {
+ return x + y;
+ }
+
+ public static Object a(Object o) {
+ return null;
+ }
+
+ public static Object a(Object o1, Object o2) {
+ return null;
+ }
+
+ public static void main(String[] args) {
+ a();
+ a(1);
+ a(1, 2);
+ a(new Object());
+ a(null);
+ a(new Integer(0), "");
+ a(null, null);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 6e03c0e..4530e2e 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.utils.AndroidApp.DEFAULT_PROGUARD_MAP_FILE;
+
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
@@ -770,13 +772,13 @@
if (dexComparator != null) {
DexInspector ref = new DexInspector(Paths.get(originalDex));
DexInspector inspector = new DexInspector(generated,
- minify ? temp.getRoot().toPath().resolve("proguard.map").toString() : null);
+ minify ? temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE).toString() : null);
dexComparator.accept(ref, inspector);
}
if (inspection != null) {
DexInspector inspector = new DexInspector(generated,
- minify ? temp.getRoot().toPath().resolve("proguard.map").toString() : null);
+ minify ? temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE).toString() : null);
inspection.accept(inspector);
}
}