Move Objects.requireNonNull and Lomg.compare handling into BackportedMethodRewriter
This adds support for arbitrary IR rewriting while handling a method that needs backported. For these two specific methods, use the same IR manipulation as before, but it is now centralized with the other, template-based backports for Objects and Long.
Test: tools/test.py --no-internal -v *Backport*Test*
Change-Id: I7e011bd2c9acbd280db39005e1fbac5849151ce1
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 db352a2..1d00f15 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
@@ -1163,7 +1163,6 @@
}
codeRewriter.rewriteKnownArrayLengthCalls(code);
- codeRewriter.rewriteLongCompareAndRequireNonNull(code, options);
codeRewriter.rewriteAssertionErrorTwoArgumentConstructor(code, options);
codeRewriter.commonSubexpressionElimination(code);
codeRewriter.simplifyArrayConstruction(code);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 4298d1b..e19e587 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -108,11 +108,10 @@
continue;
}
- DexMethod newMethod = provider.provideMethod(appView);
- iterator.replaceCurrentInstruction(
- new InvokeStatic(newMethod, invoke.outValue(), invoke.inValues()));
+ provider.rewriteInvoke(invoke, iterator, appView);
if (provider.requiresGenerationOfCode()) {
+ DexMethod newMethod = provider.provideMethod(appView);
methodProviders.putIfAbsent(newMethod, provider);
holders.add(code.method.method.holder);
}
@@ -269,9 +268,6 @@
}
private void initializeAndroidKMethodProviders(DexItemFactory factory) {
- // Note: Long.compare rewriting is handled by CodeRewriter since there is a dedicated
- // bytecode which supports the operation.
-
// Byte
DexType type = factory.boxedByteType;
// int Byte.compare(byte a, byte b)
@@ -296,6 +292,14 @@
method = factory.createMethod(type, proto, name);
addProvider(new MethodGenerator(method, IntegerMethods::new));
+ // Long
+ type = factory.boxedLongType;
+ // int Long.compare(long a, long b)
+ name = factory.createString("compare");
+ proto = factory.createProto(factory.intType, factory.longType, factory.longType);
+ method = factory.createMethod(type, proto, name);
+ addProvider(new InvokeRewriter(method, LongMethods::rewriteCompare));
+
// Boolean
type = factory.boxedBooleanType;
// int Boolean.compare(boolean a, boolean b)
@@ -346,7 +350,11 @@
method = factory.createMethod(type, proto, name);
addProvider(new MethodGenerator(method, ObjectsMethods::new));
- // Note: Objects.requireNonNull(T) rewriting is handled by CodeRewriter for now.
+ // T Objects.requireNonNull(T obj)
+ name = factory.createString("requireNonNull");
+ proto = factory.createProto(factory.objectType, factory.objectType);
+ method = factory.createMethod(type, proto, name);
+ addProvider(new InvokeRewriter(method, ObjectsMethods::rewriteRequireNonNull));
// T Objects.requireNonNull(T obj, String message)
name = factory.createString("requireNonNull");
@@ -1052,6 +1060,9 @@
this.method = method;
}
+ public abstract void rewriteInvoke(InvokeMethod invoke, InstructionListIterator iterator,
+ AppView<?> appView);
+
public abstract DexMethod provideMethod(AppView<?> appView);
public abstract TemplateMethodCode generateTemplateMethod(
@@ -1074,6 +1085,13 @@
}
@Override
+ public void rewriteInvoke(InvokeMethod invoke, InstructionListIterator iterator,
+ AppView<?> appView) {
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(provideMethod(appView), invoke.outValue(), invoke.inValues()));
+ }
+
+ @Override
public DexMethod provideMethod(AppView<?> appView) {
if (targetMethod != null) {
return targetMethod;
@@ -1096,6 +1114,33 @@
}
}
+ private static final class InvokeRewriter extends MethodProvider {
+ private final MethodInvokeRewriter rewriter;
+
+ InvokeRewriter(DexMethod method, MethodInvokeRewriter rewriter) {
+ super(method);
+ this.rewriter = rewriter;
+ }
+
+ @Override public void rewriteInvoke(InvokeMethod invoke, InstructionListIterator iterator,
+ AppView<?> appView) {
+ rewriter.rewrite(invoke, iterator, appView.dexItemFactory());
+ }
+
+ @Override public boolean requiresGenerationOfCode() {
+ return false;
+ }
+
+ @Override public DexMethod provideMethod(AppView<?> appView) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public TemplateMethodCode generateTemplateMethod(InternalOptions options, DexMethod method) {
+ throw new Unreachable();
+ }
+ }
+
private static class MethodGenerator extends MethodProvider {
private final TemplateMethodFactory factory;
@@ -1113,6 +1158,13 @@
}
@Override
+ public void rewriteInvoke(InvokeMethod invoke, InstructionListIterator iterator,
+ AppView<?> appView) {
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(provideMethod(appView), invoke.outValue(), invoke.inValues()));
+ }
+
+ @Override
public DexMethod provideMethod(AppView<?> appView) {
if (generatedMethod != null) {
return generatedMethod;
@@ -1179,4 +1231,8 @@
TemplateMethodCode create(InternalOptions options, DexMethod method, String name);
}
+
+ private interface MethodInvokeRewriter {
+ void rewrite(InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethods.java
index e3bf3b5..639f8d4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethods.java
@@ -4,9 +4,17 @@
package com.android.tools.r8.ir.desugar.backports;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.synthetic.TemplateMethodCode;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
public final class LongMethods extends TemplateMethodCode {
public LongMethods(InternalOptions options, DexMethod method, String methodName) {
@@ -198,4 +206,12 @@
return new String(buf, i, buf.length - i);
}
}
+
+ public static void rewriteCompare(InvokeMethod invoke, InstructionListIterator iterator,
+ DexItemFactory factory) {
+ List<Value> inValues = invoke.inValues();
+ assert inValues.size() == 2;
+ iterator.replaceCurrentInstruction(
+ new Cmp(NumericType.LONG, Bias.NONE, invoke.outValue(), inValues.get(0), inValues.get(1)));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
index 4fc3cb1..6ac64e9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
@@ -4,7 +4,11 @@
package com.android.tools.r8.ir.desugar.backports;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.synthetic.TemplateMethodCode;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Arrays;
@@ -128,4 +132,15 @@
}
return fromIndex;
}
+
+ public static void rewriteRequireNonNull(InvokeMethod invoke, InstructionListIterator iterator,
+ DexItemFactory factory) {
+ InvokeVirtual getClass =
+ new InvokeVirtual(factory.objectMethods.getClass, null, invoke.inValues());
+ if (invoke.outValue() != null) {
+ invoke.outValue().replaceUsers(invoke.inValues().get(0));
+ invoke.setOutValue(null);
+ }
+ iterator.replaceCurrentInstruction(getClass);
+ }
}
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 2511133..7c1053c 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
@@ -42,8 +42,6 @@
import com.android.tools.r8.ir.code.Binop;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.Cmp;
-import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
@@ -3742,39 +3740,6 @@
assert code.isConsistentSSA();
}
- public void rewriteLongCompareAndRequireNonNull(IRCode code, InternalOptions options) {
- if (options.canUseJava7CompareAndObjectsOperations()) {
- return;
- }
-
- InstructionListIterator iterator = code.instructionListIterator();
- while (iterator.hasNext()) {
- Instruction current = iterator.next();
- if (current.isInvokeMethod()) {
- DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
- if (invokedMethod == dexItemFactory.longMethods.compare) {
- // Rewrite calls to Long.compare for sdk versions that do not have that method.
- List<Value> inValues = current.inValues();
- assert inValues.size() == 2;
- iterator.replaceCurrentInstruction(
- new Cmp(NumericType.LONG, Bias.NONE, current.outValue(), inValues.get(0),
- inValues.get(1)));
- } else if (invokedMethod == dexItemFactory.objectsMethods.requireNonNull) {
- // Rewrite calls to Objects.requireNonNull(Object) because Javac 9 start to use it for
- // synthesized null checks.
- InvokeVirtual callToGetClass = new InvokeVirtual(dexItemFactory.objectMethods.getClass,
- null, current.inValues());
- if (current.outValue() != null) {
- current.outValue().replaceUsers(current.inValues().get(0));
- current.setOutValue(null);
- }
- iterator.replaceCurrentInstruction(callToGetClass);
- }
- }
- }
- assert code.isConsistentSSA();
- }
-
public void rewriteAssertionErrorTwoArgumentConstructor(IRCode code, InternalOptions options) {
if (options.canUseAssertionErrorTwoArgumentConstructor()) {
return;
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 c9407a8..e35245d 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1081,10 +1081,6 @@
return intermediate || hasMinApi(AndroidApiLevel.L);
}
- public boolean canUseJava7CompareAndObjectsOperations() {
- return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.K);
- }
-
public boolean canUseSuppressedExceptions() {
return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.K);
}
diff --git a/src/test/examples/rewrite/LongCompare.java b/src/test/examples/rewrite/LongCompare.java
deleted file mode 100644
index 7c96c10..0000000
--- a/src/test/examples/rewrite/LongCompare.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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 rewrite;
-
-public class LongCompare {
-
- public static int simpleCompare(long l1, long l2) {
- try {
- return Long.compare(l1, l2);
- } catch (Throwable t) {
- System.out.println(t);
- }
- return 2;
- }
-
- public static long getValue1() {
- return 123456789L;
- }
-
- public static long getValue2() {
- return 0;
- }
-
- public static boolean complexCompare(long l1, long l2) {
- return Long.compare(getValue1(), l1) == 0 && Long.compare(l2, getValue2()) > 0;
- }
-
- public static void main(String[] args) {
- System.out.println(simpleCompare(123456789L, 987654321L));
- System.out.println(simpleCompare(Long.MAX_VALUE, 0L));
- System.out.println(simpleCompare(Long.MIN_VALUE, 0L));
- System.out.println(simpleCompare(Long.MAX_VALUE, Long.MAX_VALUE));
-
- System.out.println(complexCompare(123456789L, 1));
- System.out.println(complexCompare(123456789L, -1));
- System.out.println(complexCompare(1234567890L, 1));
- System.out.println(complexCompare(1234567890L, -1));
- }
-}
diff --git a/src/test/examples/rewrite/RequireNonNull.java b/src/test/examples/rewrite/RequireNonNull.java
deleted file mode 100644
index d8868bf..0000000
--- a/src/test/examples/rewrite/RequireNonNull.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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 rewrite;
-
-import java.util.Objects;
-
-public class RequireNonNull {
-
- public static void main(String[] args) {
- RequireNonNull o = new RequireNonNull();
- System.out.println(o.nonnullRemove().toString());
- System.out.println(o.nonnullRemove(o).toString());
- o.nonnullWithPhiInstruction(true, o);
- o.nonnullWithPhiInstruction(false, o);
- }
-
- private Object nonnullRemove() {
- return Objects.requireNonNull(this);
- }
-
- private Object nonnullRemove(Object o) {
- Objects.requireNonNull(o);
- return o;
- }
-
- private void nonnullWithPhiInstruction(boolean b, Object input) {
- Object o = null;
- if (b) {
- o = Objects.requireNonNull(input);
- }
- System.out.println(o);
- }
-
- @Override
- public String toString() {
- return "toString";
- }
-}
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
deleted file mode 100644
index 16b7922..0000000
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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.longcompare;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
-import com.android.tools.r8.utils.StringUtils;
-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.InvokeInstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Iterator;
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-/**
- * Tests checking that Long.compare() is always rewritten into long compare instruction.
- */
-public class LongCompare {
-
- @Rule
- public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
-
- void compileWithD8(Path intputPath, Path outputPath)
- throws IOException, CompilationFailedException {
- D8.run(
- D8Command.builder()
- .addProgramFiles(intputPath)
- .setOutput(outputPath, OutputMode.DexIndexed)
- .build());
- }
-
- void runTest(Path dexFile) {
- ArtCommandBuilder builder = new ArtCommandBuilder(ToolHelper.getDexVm());
- builder.appendClasspath(dexFile.toString());
- builder.setMainClass("rewrite.LongCompare");
- try {
- String output = ToolHelper.runArt(builder);
- Assert
- .assertEquals(StringUtils.lines("-1", "1", "-1", "0", "true", "false", "false", "false"),
- output);
- } catch (IOException e) {
- Assert.fail();
- }
- }
-
- @Test
- public void testLongCompareIsRewritten() throws Exception {
- final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
- Path outputPath = tmpOutputDir.newFolder().toPath();
-
- compileWithD8(inputPath, outputPath);
-
- Path dexPath = outputPath.resolve("classes.dex");
-
- CodeInspector codeInspector = new CodeInspector(dexPath);
- ClassSubject classSubject = codeInspector.clazz("rewrite.LongCompare");
- MethodSubject methodSubject = classSubject
- .method("int", "simpleCompare", Arrays.asList("long", "long"));
- // Check that exception handler is removed since it is no longer needed.
- Assert.assertFalse(methodSubject.getMethod().getCode().asDexCode().usesExceptionHandling());
- Iterator<InvokeInstructionSubject> iterator =
- methodSubject.iterateInstructions(InstructionSubject::isInvoke);
- Assert.assertFalse(iterator.hasNext());
-
- runTest(dexPath);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
deleted file mode 100644
index 278bc8b..0000000
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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.longcompare;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-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.InvokeInstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.concurrent.ExecutionException;
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-public class RequireNonNullRewriteTest {
-
- @Rule
- public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
-
- void compileWithD8(Path intputPath, Path outputPath, CompilationMode mode)
- throws IOException, CompilationFailedException {
- D8.run(
- D8Command.builder()
- .setMode(mode)
- .addProgramFiles(intputPath)
- .setOutput(outputPath, OutputMode.DexIndexed)
- .build());
- }
-
- void runTest(Path jarFile, Path dexFile) {
- String mainClass = "rewrite.RequireNonNull";
- ArtCommandBuilder builder = new ArtCommandBuilder(ToolHelper.getDexVm());
- builder.appendClasspath(dexFile.toString());
- builder.setMainClass(mainClass);
- try {
- String output = ToolHelper.runArt(builder);
- ProcessResult javaResult = ToolHelper.runJava(jarFile, mainClass);
- Assert.assertEquals(javaResult.stdout, output);
- } catch (IOException e) {
- Assert.fail();
- }
- }
-
- @Test
- public void testDebugRequireNonNullIsRewritten() throws Exception {
- runTest(CompilationMode.DEBUG);
- }
-
- @Test
- public void testReleaseRequireNonNullIsRewritten() throws Exception {
- runTest(CompilationMode.RELEASE);
- }
-
- private void runTest(CompilationMode mode)
- throws IOException, ExecutionException, CompilationFailedException {
- final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
- Path outputPath = tmpOutputDir.newFolder().toPath();
-
- compileWithD8(inputPath, outputPath, mode);
-
- Path dexPath = outputPath.resolve("classes.dex");
-
- CodeInspector codeInspector = new CodeInspector(dexPath);
- ClassSubject classSubject = codeInspector.clazz("rewrite.RequireNonNull");
- MethodSubject methodSubject = classSubject
- .method("java.lang.Object", "nonnullRemove", Collections.emptyList());
-
- Iterator<InvokeInstructionSubject> iterator =
- methodSubject.iterateInstructions(InstructionSubject::isInvoke);
- Assert.assertTrue(iterator.hasNext());
- Assert.assertEquals("getClass", iterator.next().invokedMethod().name.toString());
- Assert.assertFalse(iterator.hasNext());
-
- runTest(inputPath, dexPath);
- }
-}