Merge "Don't preload classpath"
diff --git a/build.gradle b/build.gradle
index 1ad9b98..9539df2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1068,7 +1068,9 @@
if (project.hasProperty('dex_vm') && project.property('dex_vm') != 'default') {
println "Running with non default vm: " + project.property('dex_vm')
systemProperty 'dex_vm', project.property('dex_vm')
- if (project.property('dex_vm') == '5.1.1' || project.property('dex_vm') == '6.0.1') {
+ if (project.property('dex_vm').startsWith('4.4.4') ||
+ project.property('dex_vm').startsWith('5.1.1') ||
+ project.property('dex_vm').startsWith('6.0.1')) {
// R8 and D8 compute the dex file version number based on the input.
// Jack generates dex files with version 37 which art 5.1.1 and 6.0.1 will not run.
// Therefore we skip the jack generated art tests with those art versions.
@@ -1183,6 +1185,8 @@
// out/host/linux-x86/bin directory of the android checkout. Currently this is version pre 2.2.1,
// if that is updated the call to smali in "task "${smaliToDexTask}"(type: Exec)" below might
// need to change as smali got a completely new command line interface in version 2.2.1.
+// After Android O, Jack is no longer alive, do not forget to uncomment call to buildArtTest for
+// Jack if you build an android version using Jack.
//
// PATH=$HOME/android/master/out/host/linux-x86/bin:$PATH tools/gradle.py -Pandroid_source buildArtTests
//
@@ -1301,7 +1305,8 @@
if (!(name in skippedTestsDx)) {
dependsOn buildArtTest(androidCheckoutDir, artTestBuildDir, dir, DexTool.DX);
}
- dependsOn buildArtTest(androidCheckoutDir, artTestBuildDir, dir, DexTool.JACK);
+ // After Android O, Jack is no longer alive
+ //dependsOn buildArtTest(androidCheckoutDir, artTestBuildDir, dir, DexTool.JACK);
}
}
}
@@ -1424,7 +1429,13 @@
}
task "${smaliToDexTask}"(type: Exec) {
- workingDir "${testDir}/smali"
+ // Directory that contains smali files is either smali, or smali/art
+ def smali_dir = file("${dir}/smali/art")
+ if (smali_dir.exists()) {
+ workingDir "${testDir}/smali/art"
+ } else {
+ workingDir "${testDir}/smali"
+ }
executable "/bin/bash"
// This is the command line options for smali prior to 2.2.1, where smali got a new
// command line interface.
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 72cfbab..426197b 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -22,6 +22,7 @@
private final CompilationMode mode;
private final int minApiLevel;
private final DiagnosticsHandler diagnosticsHandler;
+ private final boolean enableDesugaring;
BaseCompilerCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
@@ -30,6 +31,7 @@
mode = null;
minApiLevel = 0;
diagnosticsHandler = new DefaultDiagnosticsHandler();
+ enableDesugaring = true;
}
BaseCompilerCommand(
@@ -38,7 +40,8 @@
OutputMode outputMode,
CompilationMode mode,
int minApiLevel,
- DiagnosticsHandler diagnosticsHandler) {
+ DiagnosticsHandler diagnosticsHandler,
+ boolean enableDesugaring) {
super(app);
assert mode != null;
assert minApiLevel > 0;
@@ -47,6 +50,7 @@
this.mode = mode;
this.minApiLevel = minApiLevel;
this.diagnosticsHandler = diagnosticsHandler;
+ this.enableDesugaring = enableDesugaring;
}
public Path getOutputPath() {
@@ -69,6 +73,10 @@
return diagnosticsHandler;
}
+ boolean getEnableDesugaring() {
+ return enableDesugaring;
+ }
+
abstract public static class Builder<C extends BaseCompilerCommand, B extends Builder<C, B>>
extends BaseCommand.Builder<C, B> {
@@ -152,6 +160,10 @@
return self();
}
+ protected boolean getEnableDesugaring() {
+ return true;
+ }
+
protected void validate() throws CompilationException {
super.validate();
if (getAppBuilder().hasMainDexList() && outputMode == OutputMode.FilePerInputClass) {
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index ac86781..3ce1ea1 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -99,6 +99,7 @@
getMode(),
getMinApiLevel(),
getDiagnosticsHandler(),
+ getEnableDesugaring(),
intermediate);
}
}
@@ -196,8 +197,10 @@
CompilationMode mode,
int minApiLevel,
DiagnosticsHandler diagnosticsHandler,
+ boolean enableDesugaring,
boolean intermediate) {
- super(inputApp, outputPath, outputMode, mode, minApiLevel, diagnosticsHandler);
+ super(inputApp, outputPath, outputMode, mode, minApiLevel, diagnosticsHandler,
+ enableDesugaring);
this.intermediate = intermediate;
}
@@ -226,6 +229,7 @@
internal.outline.enabled = false;
internal.outputMode = getOutputMode();
internal.diagnosticsHandler = getDiagnosticsHandler();
+ internal.enableDesugaring = getEnableDesugaring();
return internal;
}
}
diff --git a/src/main/java/com/android/tools/r8/Diagnostic.java b/src/main/java/com/android/tools/r8/Diagnostic.java
index 6be2748..3991c4f 100644
--- a/src/main/java/com/android/tools/r8/Diagnostic.java
+++ b/src/main/java/com/android/tools/r8/Diagnostic.java
@@ -7,5 +7,5 @@
* Interface for all diagnostic message produced by D8 and R8.
*/
public interface Diagnostic {
- String toString();
+ String getDiagnosticMessage();
}
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
index 19d6fdc..3548644 100644
--- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
+++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
@@ -16,7 +16,7 @@
* @param warning Diagnostic containing warning information.
*/
default void warning(Diagnostic warning) {
- System.err.println(warning.toString());
+ System.err.println(warning.getDiagnosticMessage());
}
/**
@@ -25,6 +25,6 @@
* @param info Diagnostic containing the information.
*/
default void info(Diagnostic info) {
- System.out.println(info.toString());
+ System.out.println(info.getDiagnosticMessage());
}
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5b28e78..c6fcc22 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -45,6 +45,7 @@
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.io.ByteStreams;
@@ -206,11 +207,11 @@
missingClasses = filterMissingClasses(
missingClasses, options.proguardConfiguration.getDontWarnPatterns());
if (!missingClasses.isEmpty()) {
- System.err.println();
- System.err.println("WARNING, some classes are missing:");
- missingClasses.forEach(clazz -> {
- System.err.println(" - " + clazz.toSourceString());
- });
+ missingClasses.forEach(
+ clazz -> {
+ options.diagnosticsHandler.warning(
+ new StringDiagnostic("Missing class: " + clazz.toSourceString()));
+ });
if (!options.ignoreMissingClasses) {
throw new CompilationError(
"Shrinking can't be performed because some library classes are missing.");
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index a6a1c33..502dd3c 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -238,6 +238,7 @@
getMode(),
getMinApiLevel(),
getDiagnosticsHandler(),
+ getEnableDesugaring(),
useTreeShaking,
useDiscardedChecker,
useMinification,
@@ -394,12 +395,14 @@
CompilationMode mode,
int minApiLevel,
DiagnosticsHandler diagnosticsHandler,
+ boolean enableDesugaring,
boolean useTreeShaking,
boolean useDiscardedChecker,
boolean useMinification,
boolean ignoreMissingClasses,
Path proguardMapOutput) {
- super(inputApp, outputPath, outputMode, mode, minApiLevel, diagnosticsHandler);
+ super(inputApp, outputPath, outputMode, mode, minApiLevel, diagnosticsHandler,
+ enableDesugaring);
assert proguardConfiguration != null;
assert mainDexKeepRules != null;
assert getOutputMode() == OutputMode.Indexed : "Only regular mode is supported in R8";
@@ -461,7 +464,6 @@
internal.keepAttributes.applyPatterns(proguardConfiguration.getKeepAttributesPatterns());
internal.ignoreMissingClasses |= proguardConfiguration.isIgnoreWarnings();
assert !internal.verbose;
- internal.verbose |= proguardConfiguration.isVerbose();
internal.mainDexKeepRules = mainDexKeepRules;
internal.minimalMainDex = internal.debug;
if (mainDexListOutput != null) {
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
new file mode 100644
index 0000000..d95de6f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -0,0 +1,158 @@
+// 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.compatdexbuilder;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Output;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public class CompatDexBuilder {
+
+ private String input = null;
+ private String output = null;
+ private int numberOfThreads = 8;
+ private boolean noLocals = false;
+ private boolean verbose = false;
+
+ public static void main(String[] args)
+ throws IOException, InterruptedException, ExecutionException {
+ new CompatDexBuilder().run(args);
+ }
+
+ private void run(String[] args) throws IOException, InterruptedException, ExecutionException {
+ System.out.println("CompatDexBuilder " + String.join(" ", args));
+
+ List<String> flags = new ArrayList<>();
+
+ for (String arg : args) {
+ if (arg.startsWith("@")) {
+ flags.addAll(Files.readAllLines(Paths.get(arg.substring(1))));
+ } else {
+ flags.add(arg);
+ }
+ }
+
+ for (int i = 0; i < flags.size(); i++) {
+ String flag = flags.get(i);
+ if (flag.startsWith("--positions")) {
+ continue;
+ }
+ if (flag.startsWith("--num-threads=")) {
+ numberOfThreads = Integer.parseInt(flag.substring("--num-threads=".length()));
+ }
+ switch (flag) {
+ case "--input_jar":
+ input = flags.get(++i);
+ break;
+ case "--output_zip":
+ output = flags.get(++i);
+ break;
+ case "--verify-dex-file":
+ case "--no-verify-dex-file":
+ case "--show_flags":
+ case "--no-optimize":
+ case "--help":
+ // Ignore
+ break;
+ case "--verbose":
+ verbose = true;
+ break;
+ case "--nolocals":
+ noLocals = true;
+ break;
+ }
+ }
+
+ if (input == null) {
+ System.err.println("No input jar specified");
+ System.exit(1);
+ }
+
+ if (output == null) {
+ System.err.println("No output jar specified");
+ System.exit(1);
+ }
+
+ ExecutorService executor = ThreadUtils.getExecutorService(numberOfThreads);
+ try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(Paths.get(output)))) {
+
+ List<ZipEntry> toDex = new ArrayList<>();
+
+ try (ZipFile zipFile = new ZipFile(input)) {
+ final Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (!entry.getName().endsWith(".class")) {
+ try (InputStream stream = zipFile.getInputStream(entry)) {
+ addEntry(entry.getName(), stream, entry.getTime(), out);
+ }
+ } else {
+ toDex.add(entry);
+ }
+ }
+
+ List<Future<D8Output>> futures = new ArrayList<>(toDex.size());
+ for (int i = 0; i < toDex.size(); i++) {
+ ZipEntry classEntry = toDex.get(i);
+ futures.add(executor.submit(() -> dexEntry(zipFile, classEntry, executor)));
+ }
+ for (int i = 0; i < futures.size(); i++) {
+ D8Output result = futures.get(i).get();
+ ZipEntry entry = toDex.get(i);
+ assert result.getDexResources().size() == 1;
+ try (InputStream dexStream = result.getDexResources().get(0).getStream()) {
+ addEntry(entry.getName() + ".dex", dexStream, entry.getTime(), out);
+ }
+ }
+ }
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ private D8Output dexEntry(ZipFile zipFile, ZipEntry classEntry, ExecutorService executor)
+ throws IOException, CompilationException {
+ try (InputStream stream = zipFile.getInputStream(classEntry)) {
+ CompatDexBuilderCommandBuilder builder = new CompatDexBuilderCommandBuilder();
+ builder
+ .addClassProgramData(ByteStreams.toByteArray(stream))
+ .setMode(noLocals ? CompilationMode.RELEASE : CompilationMode.DEBUG)
+ .setMinApiLevel(AndroidApiLevel.H_MR2.getLevel());
+ return D8.run(builder.build(), executor);
+ }
+ }
+
+ private static void addEntry(String name, InputStream in, long time, ZipOutputStream out)
+ throws IOException {
+ ZipEntry zipEntry = new ZipEntry(name);
+ byte[] bytes = ByteStreams.toByteArray(in);
+ CRC32 crc32 = new CRC32();
+ crc32.update(bytes);
+ zipEntry.setSize(bytes.length);
+ zipEntry.setMethod(ZipEntry.STORED);
+ zipEntry.setCrc(crc32.getValue());
+ zipEntry.setTime(time);
+ out.putNextEntry(zipEntry);
+ out.write(bytes);
+ out.closeEntry();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderCommandBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderCommandBuilder.java
new file mode 100644
index 0000000..5773d7d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderCommandBuilder.java
@@ -0,0 +1,17 @@
+// 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.compatdexbuilder;
+
+import com.android.tools.r8.D8Command;
+
+public class CompatDexBuilderCommandBuilder extends D8Command.Builder {
+ CompatDexBuilderCommandBuilder() {
+ super(true);
+ }
+
+ @Override
+ protected boolean getEnableDesugaring() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDxCommandBuilder.java b/src/main/java/com/android/tools/r8/compatdx/CompatDxCommandBuilder.java
index 55206a4..3e8a006 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDxCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDxCommandBuilder.java
@@ -10,4 +10,9 @@
CompatDxCommandBuilder() {
super(true);
}
+
+ @Override
+ protected boolean getEnableDesugaring() {
+ return false;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 2ea5e56..1ad589f 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -12,6 +12,16 @@
import java.nio.file.Paths;
import java.util.List;
+/**
+ * Proguard + Dx compatibility interface for r8.
+ *
+ * This should become a mostly drop-in replacement for uses of Proguard followed by Dx.
+ *
+ * It accepts all Proguard flags supported by r8, except -outjars.
+ *
+ * The flag -outjars does not make sense as r8 (like Proguard + Dx) produces Dex output.
+ * For output use --output as for R8 proper.
+ */
public class CompatProguard {
public static class CompatProguardOptions {
public final String output;
@@ -24,7 +34,7 @@
this.proguardConfig = proguardConfig;
}
- public static CompatProguardOptions parse(String[] args) {
+ public static CompatProguardOptions parse(String[] args) throws CompilationException {
String output = null;
int minApi = 1;
ImmutableList.Builder<String> builder = ImmutableList.builder();
@@ -35,8 +45,11 @@
if (arg.charAt(0) == '-') {
if (arg.equals("--min-api")) {
minApi = Integer.valueOf(args[++i]);
- } else if (arg.equals("-outjars")) {
+ } else if (arg.equals("--output")) {
output = args[++i];
+ } else if (arg.equals("-outjars")) {
+ throw new CompilationException(
+ "Proguard argument -outjar is not supported. Use R8 compatible --output flag");
} else {
builder.add(currentLine.toString());
currentLine = new StringBuilder(arg);
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 752c66b..61e1022 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -144,9 +144,9 @@
} else if (!options.canUseMultidex()
&& options.mainDexKeepRules.isEmpty()
&& application.mainDexList.isEmpty()) {
- distributor = new VirtualFile.MonoDexDistributor(this);
+ distributor = new VirtualFile.MonoDexDistributor(this, options);
} else {
- distributor = new VirtualFile.FillFilesDistributor(this, options.minimalMainDex);
+ distributor = new VirtualFile.FillFilesDistributor(this, options);
}
Map<Integer, VirtualFile> newFiles = distributor.run();
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index eeb5023..34ec68c 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -23,6 +23,8 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Iterators;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -267,9 +269,11 @@
protected Set<DexProgramClass> classes;
protected Map<DexProgramClass, String> originalNames;
protected final VirtualFile mainDexFile;
+ protected final InternalOptions options;
- DistributorBase(ApplicationWriter writer) {
+ DistributorBase(ApplicationWriter writer, InternalOptions options) {
super(writer);
+ this.options = options;
// Create the primary dex file. The distribution will add more if needed.
mainDexFile = new VirtualFile(0, writer.namingLens);
@@ -293,10 +297,11 @@
mainDexFile.addClass(programClass);
classes.remove(programClass);
} else {
- System.out.println(
- "WARNING: Application does not contain `"
- + type.toSourceString()
- + "` as referenced in main-dex-list.");
+ options.diagnosticsHandler.warning(
+ new StringDiagnostic(
+ "Application does not contain `"
+ + type.toSourceString()
+ + "` as referenced in main-dex-list."));
}
mainDexFile.commitTransaction();
}
@@ -338,12 +343,10 @@
}
public static class FillFilesDistributor extends DistributorBase {
- boolean minimalMainDex;
private final FillStrategy fillStrategy;
- FillFilesDistributor(ApplicationWriter writer, boolean minimalMainDex) {
- super(writer);
- this.minimalMainDex = minimalMainDex;
+ FillFilesDistributor(ApplicationWriter writer, InternalOptions options) {
+ super(writer, options);
this.fillStrategy = FillStrategy.FILL_MAX;
}
@@ -357,7 +360,7 @@
}
Map<Integer, VirtualFile> filesForDistribution = nameToFileMap;
- if (minimalMainDex && !mainDexFile.isEmpty()) {
+ if (options.minimalMainDex && !mainDexFile.isEmpty()) {
assert !nameToFileMap.get(0).isEmpty();
// The main dex file is filtered out, so create ensure at least one file for the remaining
// classes
@@ -378,8 +381,8 @@
}
public static class MonoDexDistributor extends DistributorBase {
- MonoDexDistributor(ApplicationWriter writer) {
- super(writer);
+ MonoDexDistributor(ApplicationWriter writer, InternalOptions options) {
+ super(writer, options);
}
@Override
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 b6fbdee..2324f8e 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
@@ -119,7 +119,7 @@
return successors;
}
- public List<BasicBlock> getNormalSucessors() {
+ public List<BasicBlock> getNormalSuccessors() {
if (!hasCatchHandlers()) {
return successors;
}
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 131e0d0..7d61783 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
@@ -6,6 +6,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.IteratorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@@ -192,11 +194,6 @@
listIterator.remove();
}
- // If splitting the normal exit block, the new block is now the normal exit block.
- if (code.getNormalExitBlock() == block) {
- code.setNormalExitBlock(newBlock);
- }
-
// Insert the new block in the block list right after the current block.
if (blocksIterator == null) {
blocks.add(blocks.indexOf(block) + 1, newBlock);
@@ -371,22 +368,22 @@
BasicBlock inlineEntry = inlinee.blocks.getFirst();
BasicBlock inlineExit = null;
- if (inlinee.getNormalExitBlock() == null) {
+ ImmutableList<BasicBlock> normalExits = inlinee.computeNormalExitBlocks();
+ if (normalExits.isEmpty()) {
assert inlineeCanThrow;
// TODO(sgjesse): Remove this restriction.
assert !invokeBlock.hasCatchHandlers();
blocksToRemove.addAll(
invokePredecessor.unlink(invokeBlock, new DominatorTree(code, blocksToRemove)));
} else {
- // Locate inlinee return.
- InstructionListIterator inlineeIterator = inlinee.getNormalExitBlock().listIterator();
- inlineeIterator.nextUntil(Instruction::isReturn);
- Return ret = inlineeIterator.previous().asReturn();
+ // Ensure and locate the single return instruction of the inlinee.
+ InstructionListIterator inlineeIterator = ensureSingleReturnInstruction(inlinee, normalExits);
- // Map return value if used.
+ // Replace the invoke value with the return value if non-void.
+ assert inlineeIterator.peekNext().isReturn();
if (invoke.outValue() != null) {
- assert !ret.isReturnVoid();
- invoke.outValue().replaceUsers(ret.returnValue());
+ Return returnInstruction = inlineeIterator.peekNext().asReturn();
+ invoke.outValue().replaceUsers(returnInstruction.returnValue());
}
// Split before return and unlink return.
@@ -439,4 +436,55 @@
return invokeSuccessor;
}
+
+ private InstructionListIterator ensureSingleReturnInstruction(
+ IRCode code,
+ ImmutableList<BasicBlock> normalExits) {
+ if (normalExits.size() == 1) {
+ InstructionListIterator it = normalExits.get(0).listIterator();
+ it.nextUntil(Instruction::isReturn);
+ it.previous();
+ return it;
+ }
+ BasicBlock newExitBlock = new BasicBlock();
+ newExitBlock.setNumber(code.getHighestBlockNumber() + 1);
+ Return newReturn;
+ if (normalExits.get(0).exit().asReturn().isReturnVoid()) {
+ newReturn = new Return();
+ } else {
+ boolean same = true;
+ List<Value> operands = new ArrayList<>(normalExits.size());
+ for (BasicBlock exitBlock : normalExits) {
+ Value retValue = exitBlock.exit().asReturn().returnValue();
+ operands.add(retValue);
+ same = same && retValue == operands.get(0);
+ }
+ Value value;
+ if (same) {
+ value = operands.get(0);
+ } else {
+ Phi phi =
+ new Phi(
+ code.valueNumberGenerator.next(),
+ newExitBlock,
+ operands.get(0).outType(),
+ null);
+ phi.addOperands(operands);
+ value = phi;
+ }
+ newReturn = new Return(value, value.outType());
+ }
+ newExitBlock.add(newReturn);
+ for (BasicBlock exitBlock : normalExits) {
+ InstructionListIterator it = exitBlock.listIterator(exitBlock.getInstructions().size());
+ Instruction oldExit = it.previous();
+ assert oldExit.isReturn();
+ it.replaceCurrentInstruction(new Goto());
+ exitBlock.link(newExitBlock);
+ }
+ newExitBlock.close(null);
+ code.blocks.add(newExitBlock);
+ assert code.isConsistentSSA();
+ return newExitBlock.listIterator();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
index 74c2768..26305b7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
@@ -86,9 +86,6 @@
iterator.remove();
}
listIterator.remove();
- if (current == code.getNormalExitBlock()) {
- code.setNormalExitBlock(null);
- }
current = null;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
index c8a17dc2..90a9b29 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
@@ -13,6 +13,7 @@
private BasicBlock[] sorted;
private BasicBlock[] doms;
+ private final BasicBlock normalExitBlock = new BasicBlock();
public DominatorTree(IRCode code) {
this(code, Collections.emptyList());
@@ -20,7 +21,15 @@
// TODO(sgjesse) Get rid of this constructor and blocksToIgnore.
DominatorTree(IRCode code, List<BasicBlock> blocksToIgnore) {
- this.sorted = code.topologicallySortedBlocks(blocksToIgnore);
+ BasicBlock[] blocks = code.topologicallySortedBlocks(blocksToIgnore);
+ // Add the internal exit block to the block list.
+ sorted = new BasicBlock[blocks.length + 1];
+ System.arraycopy(blocks, 0, sorted, 0, blocks.length);
+ sorted[blocks.length] = normalExitBlock;
+ // Link internal exit block to each actual exit block.
+ for (BasicBlock block : code.computeNormalExitBlocks()) {
+ normalExitBlock.getPredecessors().add(block);
+ }
numberBlocks();
build();
}
@@ -54,7 +63,7 @@
* @return wether {@code subject} is strictly dominated by {@code dominator}
*/
public boolean strictlyDominatedBy(BasicBlock subject, BasicBlock dominator) {
- if (subject.getNumber() == 0) {
+ if (subject.getNumber() == 0 || subject == normalExitBlock) {
return false;
}
while (true) {
@@ -87,27 +96,26 @@
return dominator;
}
- /**
- * Returns an iterator over all blocks dominated by dominator, including dominator itself.
- */
- public Iterable<BasicBlock> dominatedBlocks(BasicBlock domintator) {
- return () -> new Iterator<BasicBlock>() {
- private int current = domintator.getNumber();
+ /** Returns an iterator over all blocks dominated by dominator, including dominator itself. */
+ public Iterable<BasicBlock> dominatedBlocks(BasicBlock dominator) {
+ return () ->
+ new Iterator<BasicBlock>() {
+ private int current = dominator.getNumber();
- @Override
- public boolean hasNext() {
- return dominatedBy(sorted[current], domintator);
- }
+ @Override
+ public boolean hasNext() {
+ return dominatedBy(sorted[current], dominator);
+ }
- @Override
- public BasicBlock next() {
- if (!hasNext()) {
- return null;
- } else {
- return sorted[current++];
- }
- }
- };
+ @Override
+ public BasicBlock next() {
+ if (!hasNext()) {
+ return null;
+ } else {
+ return sorted[current++];
+ }
+ }
+ };
}
/**
@@ -143,6 +151,10 @@
};
}
+ public Iterable<BasicBlock> normalExitDominatorBlocks() {
+ return dominatorBlocks(normalExitBlock);
+ }
+
public BasicBlock[] getSortedBlocks() {
return sorted;
}
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 2e8b683..d9fdaf2 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
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
import com.android.tools.r8.utils.CfgPrinter;
+import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -27,18 +28,15 @@
public LinkedList<BasicBlock> blocks;
public final ValueNumberGenerator valueNumberGenerator;
- private BasicBlock normalExitBlock;
private boolean numbered = false;
private int nextInstructionNumber = 0;
public IRCode(
DexEncodedMethod method,
LinkedList<BasicBlock> blocks,
- BasicBlock normalExitBlock,
ValueNumberGenerator valueNumberGenerator) {
this.method = method;
this.blocks = blocks;
- this.normalExitBlock = normalExitBlock;
this.valueNumberGenerator = valueNumberGenerator;
}
@@ -110,18 +108,12 @@
BasicBlock block = blockIterator.next();
if (block.isMarked()) {
blockIterator.remove();
- if (block == normalExitBlock) {
- normalExitBlock = null;
- }
}
}
}
public void removeBlocks(List<BasicBlock> blocksToRemove) {
blocks.removeAll(blocksToRemove);
- if (blocksToRemove.contains(normalExitBlock)) {
- normalExitBlock = null;
- }
}
/**
@@ -175,7 +167,6 @@
assert consistentPredecessorSuccessors();
assert consistentCatchHandlers();
assert consistentBlockInstructions();
- assert normalExitBlock == null || normalExitBlock.exit().isReturn();
return true;
}
@@ -353,16 +344,14 @@
return new IRCodeInstructionsIterator(this);
}
- void setNormalExitBlock(BasicBlock block) {
- normalExitBlock = block;
- }
-
- public BasicBlock getNormalExitBlock() {
- return normalExitBlock;
- }
-
- public void invalidateNormalExitBlock() {
- normalExitBlock = null;
+ public ImmutableList<BasicBlock> computeNormalExitBlocks() {
+ ImmutableList.Builder<BasicBlock> builder = ImmutableList.builder();
+ for (BasicBlock block : blocks) {
+ if (block.exit().isReturn()) {
+ builder.add(block);
+ }
+ }
+ return builder.build();
}
public ListIterator<BasicBlock> listIterator() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 91b51f4..35cd7fa 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -80,7 +80,6 @@
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
@@ -262,9 +261,6 @@
private Long2ObjectMap<ConstNumber> doubleConstants = new Long2ObjectArrayMap<>();
private Long2ObjectMap<ConstNumber> nullConstants = new Long2ObjectArrayMap<>();
- private List<BasicBlock> exitBlocks = new ArrayList<>();
- private BasicBlock normalExitBlock;
-
private List<BasicBlock.Pair> needGotoToCatchBlocks = new ArrayList<>();
final private ValueNumberGenerator valueNumberGenerator;
@@ -378,9 +374,6 @@
// but before handle-exit (which does not maintain predecessor counts).
assert verifyFilledPredecessors();
- // If there are multiple returns create an exit block.
- handleExitBlock();
-
// Clear all reaching definitions to free up memory (and avoid invalid use).
for (BasicBlock block : blocks) {
block.clearCurrentDefinitions();
@@ -395,7 +388,7 @@
splitCriticalEdges();
// Package up the IR code.
- IRCode ir = new IRCode(method, blocks, normalExitBlock, valueNumberGenerator);
+ IRCode ir = new IRCode(method, blocks, valueNumberGenerator);
if (options.testing.invertConditionals) {
invertConditionalsForTesting(ir);
@@ -436,7 +429,7 @@
for (BlockInfo info : targets.values()) {
if (info != null && info.block == block) {
assert info.predecessorCount() == block.getPredecessors().size();
- assert info.normalSuccessors.size() == block.getNormalSucessors().size();
+ assert info.normalSuccessors.size() == block.getNormalSuccessors().size();
if (block.hasCatchHandlers()) {
assert info.exceptionalSuccessors.size()
== block.getCatchHandlers().getUniqueTargets().size();
@@ -1258,14 +1251,14 @@
public void addReturn(MoveType type, int value) {
Value in = readRegister(value, type);
+ source.buildPostlude(this);
addInstruction(new Return(in, type));
- exitBlocks.add(currentBlock);
closeCurrentBlock();
}
public void addReturn() {
+ source.buildPostlude(this);
addInstruction(new Return());
- exitBlocks.add(currentBlock);
closeCurrentBlock();
}
@@ -1839,78 +1832,6 @@
closeCurrentBlock();
}
- void handleExitBlock() {
- if (exitBlocks.size() > 0) {
- // Create and populate the exit block if needed (eg, synchronized support for jar).
- setCurrentBlock(new BasicBlock());
- source.buildPostlude(this);
- // If the new exit block is empty and we only have one exit, abort building a new exit block.
- if (currentBlock.getInstructions().isEmpty() && exitBlocks.size() == 1) {
- normalExitBlock = exitBlocks.get(0);
- setCurrentBlock(null);
- return;
- }
- // Commit to creating the new exit block.
- normalExitBlock = currentBlock;
- normalExitBlock.setNumber(nextBlockNumber++);
- blocks.add(normalExitBlock);
- // Add the return instruction possibly creating a phi of return values.
- Return origReturn = exitBlocks.get(0).exit().asReturn();
- Phi phi = null;
- if (origReturn.isReturnVoid()) {
- normalExitBlock.add(new Return());
- } else {
- Value returnValue = origReturn.returnValue();
- MoveType returnType = origReturn.getReturnType();
- assert origReturn.getLocalInfo() == null;
- phi = new Phi(
- valueNumberGenerator.next(), normalExitBlock, returnValue.outType(), null);
- normalExitBlock.add(new Return(phi, returnType));
- assert returnType == MoveType.fromDexType(method.method.proto.returnType);
- }
- closeCurrentBlock();
-
- // Collect the debug values which are live on all returns.
- Set<Value> debugValuesForReturn = Sets.newIdentityHashSet();
- for (Value value : exitBlocks.get(0).exit().getDebugValues()) {
- boolean include = true;
- for (int i = 1; i < exitBlocks.size() && include; i++) {
- include = exitBlocks.get(i).exit().getDebugValues().contains(value);
- }
- if (include) {
- debugValuesForReturn.add(value);
- }
- }
-
- // Move all these debug values to the new return.
- for (Value value : debugValuesForReturn) {
- for (BasicBlock block : exitBlocks) {
- block.exit().moveDebugValue(value, normalExitBlock.exit());
- }
- }
-
- // Replace each return instruction with a goto to the new exit block.
- List<Value> operands = new ArrayList<>();
- for (BasicBlock block : exitBlocks) {
- List<Instruction> instructions = block.getInstructions();
- Return ret = block.exit().asReturn();
- if (!ret.isReturnVoid()) {
- operands.add(ret.returnValue());
- ret.returnValue().removeUser(ret);
- }
- Goto gotoExit = new Goto();
- gotoExit.setBlock(block);
- ret.moveDebugValues(gotoExit);
- instructions.set(instructions.size() - 1, gotoExit);
- block.link(normalExitBlock);
- gotoExit.setTarget(normalExitBlock);
- }
- if (phi != null) {
- phi.addOperands(operands);
- }
- }
- }
-
private void handleFallthroughToCatchBlock() {
// When a catch handler for a block goes to the same block as the fallthrough for that
// block the graph only has one edge there. In these cases we add an additional block so the
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 cfc1495..184087a 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
@@ -87,7 +87,6 @@
GraphLense graphLense,
InternalOptions options,
CfgPrinter printer,
- boolean enableDesugaring,
boolean enableWholeProgramOptimizations) {
assert application != null;
assert appInfo != null;
@@ -99,9 +98,9 @@
this.options = options;
this.printer = printer;
this.codeRewriter = new CodeRewriter(appInfo, libraryMethodsReturningReceiver());
- this.lambdaRewriter = enableDesugaring ? new LambdaRewriter(this) : null;
+ this.lambdaRewriter = options.enableDesugaring ? new LambdaRewriter(this) : null;
this.interfaceMethodRewriter =
- (enableDesugaring && enableInterfaceMethodDesugaring())
+ (options.enableDesugaring && enableInterfaceMethodDesugaring())
? new InterfaceMethodRewriter(this, options) : null;
if (enableWholeProgramOptimizations) {
assert appInfo.hasSubtyping();
@@ -130,20 +129,7 @@
DexApplication application,
AppInfo appInfo,
InternalOptions options) {
- this(null, application, appInfo, null, options, null, true, false);
- }
-
- /**
- * Create an IR converter for processing methods with full program optimization disabled.
- *
- * The argument <code>enableDesugaring</code> if desugaring is enabled.
- */
- public IRConverter(
- DexApplication application,
- AppInfo appInfo,
- InternalOptions options,
- boolean enableDesugaring) {
- this(null, application, appInfo, null, options, null, enableDesugaring, false);
+ this(null, application, appInfo, null, options, null, false);
}
/**
@@ -155,7 +141,7 @@
AppInfo appInfo,
InternalOptions options,
CfgPrinter printer) {
- this(timing, application, appInfo, null, options, printer, true, false);
+ this(timing, application, appInfo, null, options, printer, false);
}
/**
@@ -168,7 +154,7 @@
InternalOptions options,
CfgPrinter printer,
GraphLense graphLense) {
- this(timing, application, appInfo, graphLense, options, printer, true, true);
+ this(timing, application, appInfo, graphLense, options, printer, true);
}
private boolean enableInterfaceMethodDesugaring() {
@@ -566,7 +552,7 @@
DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
assert code.isConsistentSSA();
- if (enableTryWithResourcesDesugaring()) {
+ if (options.enableDesugaring && enableTryWithResourcesDesugaring()) {
codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
}
@@ -595,8 +581,6 @@
printMethod(code, "Optimized IR (SSA)");
- codeRewriter.inlineReturnBlock(code);
-
// Perform register allocation.
RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
method.setCode(code, registerAllocator, appInfo.dexItemFactory);
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 810039f..8ea7b32 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
@@ -87,7 +87,6 @@
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@@ -206,7 +205,7 @@
private static int getThrowsColorForSuccessors(BasicBlock block) {
int color = CANNOT_THROW;
- for (BasicBlock successor : block.getNormalSucessors()) {
+ for (BasicBlock successor : block.getNormalSuccessors()) {
if (successor.hasColor(CAN_THROW)) {
return CAN_THROW;
}
@@ -760,24 +759,38 @@
public void identifyReturnsArgument(
DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
- if (code.getNormalExitBlock() != null) {
- Return ret = code.getNormalExitBlock().exit().asReturn();
- if (!ret.isReturnVoid()) {
- Value returnValue = ret.returnValue();
- if (returnValue.isArgument()) {
- // Find the argument number.
- int index = code.collectArguments().indexOf(returnValue);
- assert index != -1;
- feedback.methodReturnsArgument(method, index);
- }
- if (returnValue.isConstant() && returnValue.definition.isConstNumber()) {
- long value = returnValue.definition.asConstNumber().getRawValue();
- feedback.methodReturnsConstant(method, value);
- }
- if (returnValue.isNeverNull()) {
- feedback.methodNeverReturnsNull(method);
- }
+ List<BasicBlock> normalExits = code.computeNormalExitBlocks();
+ if (normalExits.isEmpty()) {
+ return;
+ }
+ Return firstExit = normalExits.get(0).exit().asReturn();
+ if (firstExit.isReturnVoid()) {
+ return;
+ }
+ Value returnValue = firstExit.returnValue();
+ boolean isNeverNull = returnValue.isNeverNull();
+ for (int i = 1; i < normalExits.size(); i++) {
+ Return exit = normalExits.get(i).exit().asReturn();
+ Value value = exit.returnValue();
+ if (value != returnValue) {
+ returnValue = null;
}
+ isNeverNull = isNeverNull && value.isNeverNull();
+ }
+ if (returnValue != null) {
+ if (returnValue.isArgument()) {
+ // Find the argument number.
+ int index = code.collectArguments().indexOf(returnValue);
+ assert index != -1;
+ feedback.methodReturnsArgument(method, index);
+ }
+ if (returnValue.isConstant() && returnValue.definition.isConstNumber()) {
+ long value = returnValue.definition.asConstNumber().getRawValue();
+ feedback.methodReturnsConstant(method, value);
+ }
+ }
+ if (isNeverNull) {
+ feedback.methodNeverReturnsNull(method);
}
}
@@ -933,14 +946,13 @@
// section 5.5, https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.5), this
// does not matter (except maybe for removal of const-string instructions, but that is
// acceptable).
- DominatorTree dominatorTree = new DominatorTree(code);
- BasicBlock exit = code.getNormalExitBlock();
- if (exit == null) {
+ if (code.computeNormalExitBlocks().isEmpty()) {
return;
}
+ DominatorTree dominatorTree = new DominatorTree(code);
Set<StaticPut> puts = Sets.newIdentityHashSet();
Map<DexField, StaticPut> dominatingPuts = Maps.newIdentityHashMap();
- for (BasicBlock block : dominatorTree.dominatorBlocks(exit)) {
+ for (BasicBlock block : dominatorTree.normalExitDominatorBlocks()) {
InstructionListIterator iterator = block.listIterator(block.getInstructions().size());
while (iterator.hasPrevious()) {
Instruction current = iterator.previous();
@@ -1036,7 +1048,7 @@
// Remove the static put instructions now replaced by static filed initial values.
List<Instruction> toRemove = new ArrayList<>();
- for (BasicBlock block : dominatorTree.dominatorBlocks(exit)) {
+ for (BasicBlock block : dominatorTree.normalExitDominatorBlocks()) {
InstructionListIterator iterator = block.listIterator();
while (iterator.hasNext()) {
Instruction current = iterator.next();
@@ -1059,7 +1071,7 @@
// Remove the instructions collected for removal.
if (toRemove.size() > 0) {
- for (BasicBlock block : dominatorTree.dominatorBlocks(exit)) {
+ for (BasicBlock block : dominatorTree.normalExitDominatorBlocks()) {
InstructionListIterator iterator = block.listIterator();
while (iterator.hasNext()) {
if (toRemove.contains(iterator.next())) {
@@ -1514,87 +1526,6 @@
}
}
- /**
- * Inline the return block at its targets.
- *
- * The inlining of return mostly undoes the merge performed at IR build time. This helps avoid
- * unneeded moves as values are forced into the same register at all returns, which there can be
- * a lot of when compiling in debug mode. Measurements show that iterating the inlining of returns
- * does not pay off as it can lead to code size increase, eg, when a sequence of ifs all jump to
- * a common return.
- */
- public void inlineReturnBlock(IRCode code) {
- BasicBlock block = code.getNormalExitBlock();
- code.invalidateNormalExitBlock();
- if (block == null
- || block.getPredecessors().size() <= 1
- || block.getInstructions().size() > 1) {
- return;
- }
- int predIndex = 0;
- for (BasicBlock pred : block.getPredecessors()) {
- ListIterator<Instruction> iterator = pred.listIterator(pred.exit());
- iterator.previous();
- for (Instruction origInstruction : block.getInstructions()) {
- assert origInstruction.isReturn();
- // Create an instruction copy replacing phi values of this block by their operands.
- Instruction instruction;
- Return ret = origInstruction.asReturn();
- if (ret.isReturnVoid()) {
- instruction = new Return();
- } else {
- Value origValue = ret.returnValue();
- Value copyValue = origValue.isPhi() && block.getPhis().contains(origValue)
- ? origValue.asPhi().getOperand(predIndex)
- : origValue;
- instruction = new Return(copyValue, ret.getReturnType());
- }
- // Copy over each debug value replacing phi values of this block by their operands.
- for (Value value : origInstruction.getDebugValues()) {
- assert value.hasLocalInfo();
- if (value.isPhi() && block.getPhis().contains(value)) {
- Phi phi = value.asPhi();
- Value operand = phi.getOperand(predIndex);
- if (phi.getLocalInfo() == operand.getLocalInfo()) {
- instruction.addDebugValue(operand);
- } else {
- // If the phi and its operand are different locals insert a local write.
- Value localValue = code.createValue(phi.outType(), phi.getLocalInfo());
- DebugLocalWrite write = new DebugLocalWrite(localValue, operand);
- write.setBlock(pred);
- iterator.add(write);
- instruction.addDebugValue(localValue);
- }
- } else {
- instruction.addDebugValue(value);
- }
- }
- instruction.setBlock(pred);
- iterator.add(instruction);
- }
- iterator.previous();
- Instruction ret = iterator.next();
- Instruction jump = iterator.next();
- assert !iterator.hasNext();
- jump.moveDebugValues(ret);
- iterator.remove();
- assert pred.exit().isReturn();
- pred.removeSuccessor(block);
- predIndex++;
- }
- // Clean out all users and remove the inlined block.
- while (!block.getPredecessors().isEmpty()) {
- block.removePredecessor(block.getPredecessors().get(0));
- }
- for (Instruction instruction : block.getInstructions()) {
- for (Value value : instruction.inValues()) {
- value.removeUser(instruction);
- }
- instruction.clearDebugValues();
- }
- code.removeBlocks(Collections.singletonList(block));
- }
-
private static class ExpressionEquivalence extends Equivalence<Instruction> {
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 5d1cbe5..9699ec1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
@@ -361,7 +360,7 @@
.buildIR(code.valueNumberGenerator, appInfo, graphLense, options);
if (inlinee != null) {
// TODO(64432527): Get rid of this additional check by improved inlining.
- if (block.hasCatchHandlers() && inlinee.getNormalExitBlock() == null) {
+ if (block.hasCatchHandlers() && inlinee.computeNormalExitBlocks().isEmpty()) {
continue;
}
// If this code did not go through the full pipeline, apply inlining to make sure
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
index 35cadf2..e6916b8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.regalloc.LiveIntervals;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
@@ -46,6 +47,14 @@
*/
private static void shareIdenticalBlockSuffix(IRCode code, RegisterAllocator allocator) {
Collection<BasicBlock> blocks = code.blocks;
+ BasicBlock normalExit = null;
+ ImmutableList<BasicBlock> normalExits = code.computeNormalExitBlocks();
+ if (normalExits.size() > 1) {
+ normalExit = new BasicBlock();
+ normalExit.getPredecessors().addAll(normalExits);
+ blocks = new ArrayList<>(code.blocks);
+ blocks.add(normalExit);
+ }
do {
int startNumberOfNewBlock = code.getHighestBlockNumber() + 1;
Map<BasicBlock, BasicBlock> newBlocks = new IdentityHashMap<>();
@@ -60,14 +69,22 @@
// and allow both a goto edge and exception edges when the target does not start with a
// MoveException instruction. However, that would require us to require rewriting of
// catch handlers as well.
- if (pred.exit().isGoto() &&
- pred.getSuccessors().size() == 1 &&
- pred.getInstructions().size() > 1) {
+ if (pred.exit().isGoto()
+ && pred.getSuccessors().size() == 1
+ && pred.getInstructions().size() > 1) {
List<Instruction> instructions = pred.getInstructions();
Instruction lastInstruction = instructions.get(instructions.size() - 2);
List<BasicBlock> value = lastInstructionToBlocks.computeIfAbsent(
equivalence.wrap(lastInstruction), (k) -> new ArrayList<>());
value.add(pred);
+ } else if (pred.exit().isReturn()
+ && pred.getSuccessors().isEmpty()
+ && pred.getInstructions().size() > 2) {
+ Instruction lastInstruction = pred.exit();
+ List<BasicBlock> value =
+ lastInstructionToBlocks.computeIfAbsent(
+ equivalence.wrap(lastInstruction), (k) -> new ArrayList<>());
+ value.add(pred);
}
}
// For each group of predecessors of size 2 or more, find the largest common suffix and
@@ -80,16 +97,20 @@
int commonSuffixSize = firstPred.getInstructions().size();
for (int i = 1; i < predsWithSameLastInstruction.size(); i++) {
BasicBlock pred = predsWithSameLastInstruction.get(i);
- assert pred.exit().isGoto();
- commonSuffixSize = Math.min(
- commonSuffixSize, sharedSuffixSizeExcludingExit(firstPred, pred, allocator));
+ assert pred.exit().isGoto() || pred.exit().isReturn();
+ commonSuffixSize =
+ Math.min(commonSuffixSize, sharedSuffixSize(firstPred, pred, allocator));
}
- if (commonSuffixSize == 0) {
+ if (commonSuffixSize <= 1) {
continue;
}
int blockNumber = startNumberOfNewBlock + newBlocks.size();
- BasicBlock newBlock = createAndInsertBlockForSuffix(
- blockNumber, commonSuffixSize, predsWithSameLastInstruction, block);
+ BasicBlock newBlock =
+ createAndInsertBlockForSuffix(
+ blockNumber,
+ commonSuffixSize,
+ predsWithSameLastInstruction,
+ block == normalExit ? null : block);
newBlocks.put(predsWithSameLastInstruction.get(0), newBlock);
}
}
@@ -109,19 +130,26 @@
int blockNumber, int suffixSize, List<BasicBlock> preds, BasicBlock successorBlock) {
BasicBlock newBlock = BasicBlock.createGotoBlock(blockNumber);
BasicBlock first = preds.get(0);
- InstructionListIterator from = first.listIterator(first.getInstructions().size() - 1);
- Int2ReferenceMap<DebugLocalInfo> newBlockEntryLocals = successorBlock.getLocalsAtEntry() == null
- ? null
- : new Int2ReferenceOpenHashMap<>(successorBlock.getLocalsAtEntry());
+ assert (successorBlock != null && first.exit().isGoto())
+ || (successorBlock == null && first.exit().isReturn());
+ int offsetFromEnd = successorBlock == null ? 0 : 1;
+ if (successorBlock == null) {
+ newBlock.getInstructions().removeLast();
+ }
+ InstructionListIterator from =
+ first.listIterator(first.getInstructions().size() - offsetFromEnd);
+ Int2ReferenceMap<DebugLocalInfo> newBlockEntryLocals =
+ (successorBlock == null || successorBlock.getLocalsAtEntry() == null)
+ ? new Int2ReferenceOpenHashMap<>()
+ : new Int2ReferenceOpenHashMap<>(successorBlock.getLocalsAtEntry());
boolean movedThrowingInstruction = false;
- for (int i = 0; i < suffixSize; i++) {
+ for (int i = offsetFromEnd; i < suffixSize; i++) {
Instruction instruction = from.previous();
movedThrowingInstruction = movedThrowingInstruction || instruction.instructionTypeCanThrow();
newBlock.getInstructions().addFirst(instruction);
instruction.setBlock(newBlock);
if (instruction.isDebugLocalsChange()) {
// Replay the debug local changes backwards to compute the entry state.
- assert newBlockEntryLocals != null;
DebugLocalsChange change = instruction.asDebugLocalsChange();
for (int starting : change.getStarting().keySet()) {
newBlockEntryLocals.remove(starting);
@@ -136,27 +164,35 @@
}
for (BasicBlock pred : preds) {
LinkedList<Instruction> instructions = pred.getInstructions();
- Instruction exit = instructions.removeLast();
for (int i = 0; i < suffixSize; i++) {
instructions.removeLast();
}
- instructions.add(exit);
+ Goto jump = new Goto();
+ jump.setBlock(pred);
+ instructions.add(jump);
newBlock.getPredecessors().add(pred);
- pred.replaceSuccessor(successorBlock, newBlock);
- successorBlock.getPredecessors().remove(pred);
+ if (successorBlock != null) {
+ pred.replaceSuccessor(successorBlock, newBlock);
+ successorBlock.getPredecessors().remove(pred);
+ } else {
+ pred.getSuccessors().add(newBlock);
+ }
if (movedThrowingInstruction) {
pred.clearCatchHandlers();
}
}
newBlock.setLocalsAtEntry(newBlockEntryLocals);
- newBlock.link(successorBlock);
+ if (successorBlock != null) {
+ newBlock.link(successorBlock);
+ }
return newBlock;
}
- private static int sharedSuffixSizeExcludingExit(
+ private static int sharedSuffixSize(
BasicBlock block0, BasicBlock block1, RegisterAllocator allocator) {
- InstructionListIterator it0 = block0.listIterator(block0.getInstructions().size() - 1);
- InstructionListIterator it1 = block1.listIterator(block1.getInstructions().size() - 1);
+ assert block0.exit().isGoto() || block0.exit().isReturn();
+ InstructionListIterator it0 = block0.listIterator(block0.getInstructions().size());
+ InstructionListIterator it1 = block1.listIterator(block1.getInstructions().size());
int suffixSize = 0;
while (it0.hasPrevious() && it1.hasPrevious()) {
Instruction i0 = it0.previous();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 2560fd6..239f9bc 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -45,6 +45,7 @@
private Path classObfuscationDictionary;
private Path packageObfuscationDictionary;
private boolean useUniqueClassMemberNames;
+ private boolean keepParameterNames;
private Builder(DexItemFactory dexItemFactory) {
this.dexItemFactory = dexItemFactory;
@@ -164,6 +165,14 @@
return useUniqueClassMemberNames;
}
+ public void setKeepParameterNames(boolean keepParameterNames) {
+ this.keepParameterNames = keepParameterNames;
+ }
+
+ boolean isKeepParameterNames() {
+ return keepParameterNames;
+ }
+
public ProguardConfiguration build() {
return new ProguardConfiguration(
dexItemFactory,
@@ -191,7 +200,8 @@
DictionaryReader.readAllNames(obfuscationDictionary),
DictionaryReader.readAllNames(classObfuscationDictionary),
DictionaryReader.readAllNames(packageObfuscationDictionary),
- useUniqueClassMemberNames);
+ useUniqueClassMemberNames,
+ keepParameterNames);
}
}
@@ -221,6 +231,7 @@
private final ImmutableList<String> classObfuscationDictionary;
private final ImmutableList<String> packageObfuscationDictionary;
private boolean useUniqueClassMemberNames;
+ private boolean keepParameterNames;
private ProguardConfiguration(
DexItemFactory factory,
@@ -248,7 +259,8 @@
ImmutableList<String> obfuscationDictionary,
ImmutableList<String> classObfuscationDictionary,
ImmutableList<String> packageObfuscationDictionary,
- boolean useUniqueClassMemberNames) {
+ boolean useUniqueClassMemberNames,
+ boolean keepParameterNames) {
this.dexItemFactory = factory;
this.injars = ImmutableList.copyOf(injars);
this.libraryjars = ImmutableList.copyOf(libraryjars);
@@ -275,6 +287,7 @@
this.classObfuscationDictionary = classObfuscationDictionary;
this.packageObfuscationDictionary = packageObfuscationDictionary;
this.useUniqueClassMemberNames = useUniqueClassMemberNames;
+ this.keepParameterNames = keepParameterNames;
}
/**
@@ -388,6 +401,10 @@
return useUniqueClassMemberNames;
}
+ public boolean isKeepParameterNames() {
+ return keepParameterNames;
+ }
+
public static ProguardConfiguration defaultConfiguration(DexItemFactory dexItemFactory) {
return new DefaultProguardConfiguration(dexItemFactory);
}
@@ -420,7 +437,8 @@
ImmutableList.of() /* obfuscationDictionary */,
ImmutableList.of() /* classObfuscationDictionary */,
ImmutableList.of() /* packageObfuscationDictionary */,
- false /* useUniqueClassMemberNames*/);
+ false /* useUniqueClassMemberNames*/,
+ false /* keepParameterNames */);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 4784387..08a7eca 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -85,6 +85,13 @@
throw new ProguardRuleParserException("-useuniqueulassmembernames is not supported");
}
+ if (configurationBuilder.isKeepParameterNames()
+ && configurationBuilder.isObfuscating()) {
+ // The flag -keepparameternames has only effect when minifying, so ignore it if we
+ // are not.
+ throw new ProguardRuleParserException("-keepparameternames is not supported");
+ }
+
return configurationBuilder.build();
}
@@ -156,6 +163,8 @@
} else if (acceptString("keeppackagenames")) {
ProguardKeepPackageNamesRule rule = parseKeepPackageNamesRule();
configurationBuilder.addRule(rule);
+ } else if (acceptString("keepparameternames")) {
+ configurationBuilder.setKeepParameterNames(true);
} else if (acceptString("checkdiscard")) {
ProguardCheckDiscardRule rule = parseCheckDiscardRule();
configurationBuilder.addRule(rule);
diff --git a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
index 09bbad6..e7baec9 100644
--- a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
@@ -613,7 +613,7 @@
if (needsCleanup) {
DominatorTree updatedTree = new DominatorTree(code);
BasicBlock fallThrough = switchInstr.fallthroughBlock();
- List<BasicBlock> successors = ImmutableList.copyOf(current.getNormalSucessors());
+ List<BasicBlock> successors = ImmutableList.copyOf(current.getNormalSuccessors());
for (BasicBlock successor : successors) {
if (successor != fallThrough && !liveBlocks.contains(successor)) {
deadBlocks.addAll(current.unlink(successor, updatedTree));
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 5c4909a..c8308a9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -86,6 +86,8 @@
public boolean intermediate = false;
public List<String> logArgumentsFilter = ImmutableList.of();
+ // Flag to turn on/off desugaring in D8/R8.
+ public boolean enableDesugaring = true;
// Defines interface method rewriter behavior.
public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Auto;
// Defines try-with-resources rewriter behavior.
diff --git a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
index 0606be2..9c690ff 100644
--- a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
@@ -13,7 +13,7 @@
}
@Override
- public String toString() {
+ public String getDiagnosticMessage() {
return message;
}
}
diff --git a/src/test/java/com/android/tools/r8/DeviceRunner.java b/src/test/java/com/android/tools/r8/DeviceRunner.java
index fc91032..c68c8f2 100644
--- a/src/test/java/com/android/tools/r8/DeviceRunner.java
+++ b/src/test/java/com/android/tools/r8/DeviceRunner.java
@@ -3,9 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.base.Joiner;
-
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.FileListingService;
@@ -14,7 +11,8 @@
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.SyncException;
import com.android.ddmlib.TimeoutException;
-
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.base.Joiner;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -320,6 +318,7 @@
}
}
+ @SuppressWarnings("deprecation")
private void executeShellCommand(
String command,
IDevice device,
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 5fe7c16..140e18f 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -48,6 +48,7 @@
import java.util.concurrent.ExecutionException;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
+import org.junit.AssumptionViolatedException;
import org.junit.ComparisonFailure;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
@@ -973,7 +974,8 @@
this.nativeLibrary = nativeLibrary;
this.directory = directory;
this.skipArt = skipArt;
- this.skipTest = skipTest;
+ this.skipTest =
+ skipTest || (ToolHelper.isWindows() && ToolHelper.getDexVm().getKind() == Kind.HOST);
this.failsWithX8 = failsWithX8;
this.failsWithArt = failsWithArt;
this.failsWithArtOutput = failsWithArtOutput;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index b97bd7b..79be8a8 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -475,7 +475,7 @@
return ImmutableSet.of(artVersionEnum);
} else {
if (isWindows()) {
- throw new RuntimeException("You need to specify a runtime with 'dex_vm' property");
+ return Collections.emptySet();
} else if (isLinux()) {
return ART_BINARY_VERSIONS.keySet();
} else {
@@ -499,11 +499,6 @@
public static DexVm getDexVm() {
String artVersion = System.getProperty("dex_vm");
if (artVersion == null) {
- if (isWindows()) {
- throw new RuntimeException(
- "Default Art version is not supported on Windows. Please specify a non-host runtime "
- + "with property 'dex_vm'");
- }
return DexVm.ART_DEFAULT;
} else {
DexVm artVersionEnum = DexVm.fromShortName(artVersion);
@@ -543,11 +538,15 @@
}
public static boolean artSupported() {
- if (!isLinux() && !isMac() && !isWindows()) {
+ if (!isLinux() && !isMac() && !isWindows()) {
System.err.println("Testing on your platform is not fully supported. " +
"Art does not work on on your platform.");
return false;
}
+ if (isWindows() && getDexVm().getKind() == Kind.HOST) {
+ System.err.println("Testing on host is not supported on Windows.");
+ return false;
+ }
return true;
}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index a9b945d..ec32bd3 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -94,8 +94,6 @@
// Set to true to enable verbose logs
private static final boolean DEBUG_TESTS = false;
- private static final List<DexVm> UNSUPPORTED_ART_VERSIONS = ImmutableList.of();
-
private static final Path JDWP_JAR = ToolHelper
.getJdwpTestsJarPath(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
private static final Path DEBUGGEE_JAR = Paths
@@ -292,9 +290,6 @@
Assume.assumeTrue("Skipping test " + testName.getMethodName()
+ " because debug tests are not yet supported on Windows",
!ToolHelper.isWindows());
- Assume.assumeFalse(
- "Skipping failing test " + testName.getMethodName() + " for runtime " + ToolHelper
- .getDexVm(), UNSUPPORTED_ART_VERSIONS.contains(ToolHelper.getDexVm()));
String[] paths = new String[extraPaths.size() + 2];
int indexPath = 0;
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index 6fb5db1..8258324 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -736,55 +736,58 @@
private void runInlineAlwaysThrowsMultiple(boolean twoGuards, int expectedA, int expectedB)
throws Exception {
- // Run code without inlining.
- TestApplication test = codeForInlineAlwaysThrows(twoGuards);
- String result = test.run();
- assertEquals(Integer.toString(expectedA), result);
-
- InstructionListIterator iterator;
-
- // Run code inlining all invokes with a.
- test = codeForInlineAlwaysThrowsMultiple(twoGuards);
- ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
- Iterator<IRCode> inlinee = test.additionalCode.listIterator(); // IR code for a's.
- List<BasicBlock> blocksToRemove = new ArrayList<>();
- while (blocksIterator.hasNext()) {
- BasicBlock block = blocksIterator.next();
- if (blocksToRemove.contains(block)) {
- continue;
- }
- iterator = block.listIterator();
- Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
- if (invoke != null) {
- iterator.previous();
- iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove, null);
- assert !blocksToRemove.isEmpty();
- }
+ {
+ // Run code without inlining.
+ TestApplication test = codeForInlineAlwaysThrows(twoGuards);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
}
- test.code.removeBlocks(blocksToRemove);
- result = test.run();
- assertEquals(Integer.toString(expectedA), result);
-
- // Run code inlining all invokes with b.
- test = codeForInlineAlwaysThrowsMultiple(twoGuards);
- blocksIterator = test.code.blocks.listIterator();
- inlinee = test.additionalCode.listIterator(3); // IR code for b's.
- while (blocksIterator.hasNext()) {
- BasicBlock block = blocksIterator.next();
- if (blocksToRemove.contains(block)) {
- continue;
+ {
+ // Run code inlining all invokes with a.
+ TestApplication test = codeForInlineAlwaysThrowsMultiple(twoGuards);
+ ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+ Iterator<IRCode> inlinee = test.additionalCode.listIterator(); // IR code for a's.
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ InstructionListIterator iterator;
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove, null);
+ }
}
- iterator = block.listIterator();
- Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
- if (invoke != null) {
- iterator.previous();
- iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove, null);
- assert !blocksToRemove.isEmpty();
- }
+ test.code.removeBlocks(blocksToRemove);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
}
- test.code.removeBlocks(blocksToRemove);
- result = test.run();
- assertEquals(Integer.toString(expectedB), result);
+ {
+ // Run code inlining all invokes with b.
+ TestApplication test = codeForInlineAlwaysThrowsMultiple(twoGuards);
+ ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+ Iterator<IRCode> inlinee = test.additionalCode.listIterator(3); // IR code for b's.
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ InstructionListIterator iterator;
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove, null);
+ }
+ }
+ test.code.removeBlocks(blocksToRemove);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
}
@Test
@@ -890,55 +893,58 @@
private void runInlineAlwaysThrowsMultipleWithControlFlow(
int a, boolean twoGuards, int expectedA, int expectedB) throws Exception {
- // Run code without inlining.
- TestApplication test = codeForInlineAlwaysThrows(twoGuards);
- String result = test.run();
- assertEquals(Integer.toString(expectedA), result);
-
- InstructionListIterator iterator;
-
- // Run code inlining all invokes with a.
- test = codeForInlineAlwaysThrowsMultipleWithControlFlow(a, twoGuards);
- ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
- Iterator<IRCode> inlinee = test.additionalCode.listIterator(); // IR code for a's.
- List<BasicBlock> blocksToRemove = new ArrayList<>();
- while (blocksIterator.hasNext()) {
- BasicBlock block = blocksIterator.next();
- if (blocksToRemove.contains(block)) {
- continue;
- }
- iterator = block.listIterator();
- Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
- if (invoke != null) {
- iterator.previous();
- iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove, null);
- assert !blocksToRemove.isEmpty();
- }
+ {
+ // Run code without inlining.
+ TestApplication test = codeForInlineAlwaysThrows(twoGuards);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
}
- test.code.removeBlocks(blocksToRemove);
- result = test.run();
- assertEquals(Integer.toString(expectedA), result);
-
- // Run code inlining all invokes with b.
- test = codeForInlineAlwaysThrowsMultipleWithControlFlow(a, twoGuards);
- blocksIterator = test.code.blocks.listIterator();
- inlinee = test.additionalCode.listIterator(3); // IR code for b's.
- while (blocksIterator.hasNext()) {
- BasicBlock block = blocksIterator.next();
- if (blocksToRemove.contains(block)) {
- continue;
+ {
+ // Run code inlining all invokes with a.
+ TestApplication test = codeForInlineAlwaysThrowsMultipleWithControlFlow(a, twoGuards);
+ ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+ Iterator<IRCode> inlinee = test.additionalCode.listIterator(); // IR code for a's.
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ InstructionListIterator iterator;
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove, null);
+ }
}
- iterator = block.listIterator();
- Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
- if (invoke != null) {
- iterator.previous();
- iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove, null);
- assert !blocksToRemove.isEmpty();
- }
+ test.code.removeBlocks(blocksToRemove);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedA), result);
}
- test.code.removeBlocks(blocksToRemove);
- result = test.run();
- assertEquals(Integer.toString(expectedB), result);
+ {
+ // Run code inlining all invokes with b.
+ TestApplication test = codeForInlineAlwaysThrowsMultipleWithControlFlow(a, twoGuards);
+ ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+ Iterator<IRCode> inlinee = test.additionalCode.listIterator(3); // IR code for b's.
+ List<BasicBlock> blocksToRemove = new ArrayList<>();
+ InstructionListIterator iterator;
+ while (blocksIterator.hasNext()) {
+ BasicBlock block = blocksIterator.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
+ iterator = block.listIterator();
+ Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+ if (invoke != null) {
+ iterator.previous();
+ iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove, null);
+ }
+ }
+ test.code.removeBlocks(blocksToRemove);
+ String result = test.run();
+ assertEquals(Integer.toString(expectedB), result);
+ }
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index 04a55b5..c911d37 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
@@ -311,7 +312,7 @@
}
public void runWithIfTest(boolean hitTrueBranch) throws Exception {
- final int initialBlockCount = 4;
+ final int initialBlockCount = 3;
final int argumentInstructions = 2;
final int firstBlockInstructions = 3;
// Try split between all non-argument instructions in the first block.
@@ -352,24 +353,27 @@
public void splitBeforeReturn(boolean hitTrueBranch) throws Exception {
TestApplication test = codeWithIf(hitTrueBranch);
IRCode code = test.code;
- // Locate the exit block and split before the return (the first instruction in the block).
- BasicBlock originalReturnBlock = code.getNormalExitBlock();
- BasicBlock newReturnBlock = originalReturnBlock.listIterator().split(code);
- // Modify the code to make the inserted block add the constant 10 to the original return value.
- Value newConstValue = new Value(test.valueNumberGenerator.next(), MoveType.SINGLE, null);
- Value newReturnValue = new Value(test.valueNumberGenerator.next(), MoveType.SINGLE, null);
- Value oldReturnValue = newReturnBlock.listIterator().next().asReturn().returnValue();
- newReturnBlock.listIterator().next().asReturn().returnValue().replaceUsers(newReturnValue);
- Instruction constInstruction = new ConstNumber(ConstType.INT, newConstValue, 10);
- Instruction addInstruction = new Add(
- NumericType.INT,
- newReturnValue,
- oldReturnValue,
- newConstValue);
- InstructionListIterator iterator = originalReturnBlock.listIterator();
- iterator.add(constInstruction);
- iterator.add(addInstruction);
-
+ // Locate the exit blocks and split before the return.
+ List<BasicBlock> exitBlocks = new ArrayList<>(code.computeNormalExitBlocks());
+ for (BasicBlock originalReturnBlock : exitBlocks) {
+ InstructionListIterator iterator =
+ originalReturnBlock.listIterator(originalReturnBlock.getInstructions().size());
+ Instruction ret = iterator.previous();
+ assert ret.isReturn();
+ BasicBlock newReturnBlock = iterator.split(code);
+ // Modify the code to make the inserted block add the constant 10 to the original return
+ // value.
+ Value newConstValue = new Value(test.valueNumberGenerator.next(), MoveType.SINGLE, null);
+ Value newReturnValue = new Value(test.valueNumberGenerator.next(), MoveType.SINGLE, null);
+ Value oldReturnValue = newReturnBlock.listIterator().next().asReturn().returnValue();
+ newReturnBlock.listIterator().next().asReturn().returnValue().replaceUsers(newReturnValue);
+ Instruction constInstruction = new ConstNumber(ConstType.INT, newConstValue, 10);
+ Instruction addInstruction =
+ new Add(NumericType.INT, newReturnValue, oldReturnValue, newConstValue);
+ iterator.previous();
+ iterator.add(constInstruction);
+ iterator.add(addInstruction);
+ }
// Run code and check result (code in the test object is updated).
String result = test.run();
assertEquals(hitTrueBranch ? "10" : "11", result);
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 9f19536..3f44025 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -680,4 +680,40 @@
config = parser.getConfig();
assertTrue(config.isUseUniqueClassMemberNames());
}
+
+ @Test
+ public void parseKeepParameterNames() throws Exception {
+ try {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
+ "-keepparameternames"
+ )));
+ parser.getConfig();
+ fail();
+ } catch (ProguardRuleParserException e) {
+ System.out.println(e);
+ assertTrue(e.getMessage().contains("-keepparameternames is not supported"));
+ }
+ }
+
+ @Test
+ public void parseKeepParameterNamesWithoutMinification() throws Exception {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
+ "-keepparameternames",
+ "-dontobfuscate"
+ )));
+ ProguardConfiguration config = parser.getConfig();
+ assertTrue(config.isKeepParameterNames());
+
+ parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
+ "-keepparameternames"
+ )));
+ parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
+ "-dontobfuscate"
+ )));
+ config = parser.getConfig();
+ assertTrue(config.isKeepParameterNames());
+ }
}
diff --git a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
index 9c41f21..5dc1e75 100644
--- a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
+++ b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
@@ -444,6 +444,6 @@
// TODO(sgjesse): Maybe this test is too fragile, as it leaves quite a lot of code, so the
// expectation might need changing with other optimizations.
// TODO(zerny): Consider optimizing the fallthrough branch of conditionals to not be return.
- assertEquals(28, code.instructions.length);
+ assertEquals(27, code.instructions.length);
}
}