Merge "Delete field nextUnusedRegisterNumber"
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 5f27356..b5ec328 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -673,6 +673,13 @@
       internal.outline.enabled = false;
     }
 
+    // Setup a configuration consumer.
+    if (proguardConfiguration.isPrintConfiguration()) {
+      internal.configurationConsumer = proguardConfiguration.getPrintConfigurationFile() != null
+          ? new StringConsumer.FileConsumer(proguardConfiguration.getPrintConfigurationFile())
+          : new StringConsumer.StreamConsumer(StandardOutOrigin.instance(), System.out);
+    }
+
     // Setup a usage information consumer.
     if (proguardConfiguration.isPrintUsage()) {
       internal.usageInformationConsumer = proguardConfiguration.getPrintUsageFile() != null
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index eb6718d..3639d9e 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "v1.2.7-dev";
+  public static final String LABEL = "v1.2.8-dev";
 
   private Version() {
   }
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 b413bfe..30664a2 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -260,6 +260,11 @@
       String deadCode,
       ProguardMapSupplier proguardMapSupplier,
       String proguardSeedsData) {
+    if (options.configurationConsumer != null) {
+      ExceptionUtils.withConsumeResourceHandler(
+          options.reporter, options.configurationConsumer,
+          options.proguardConfiguration.getParsedConfiguration());
+    }
     if (options.usageInformationConsumer != null && deadCode != null) {
       ExceptionUtils.withConsumeResourceHandler(
           options.reporter, options.usageInformationConsumer, deadCode);
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 2613aae..4adcf5a 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
@@ -13,6 +13,7 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.Deque;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
@@ -252,25 +253,44 @@
    * no sorting.
    */
   public ImmutableList<BasicBlock> topologicallySortedBlocks() {
-    Set<BasicBlock> visitedBlock = new HashSet<>();
-    ImmutableList.Builder<BasicBlock> builder = ImmutableList.builder();
-    BasicBlock entryBlock = blocks.getFirst();
-    depthFirstSorting(visitedBlock, entryBlock, builder);
-    ImmutableList<BasicBlock> ordered = builder.build().reverse();
+    ImmutableList<BasicBlock> ordered = depthFirstSorting();
     return options.testing.placeExceptionalBlocksLast
         ? reorderExceptionalBlocksLastForTesting(ordered)
         : ordered;
   }
 
-  private void depthFirstSorting(Set<BasicBlock> visitedBlock, BasicBlock block,
-      ImmutableList.Builder<BasicBlock> builder) {
-    if (!visitedBlock.contains(block)) {
-      visitedBlock.add(block);
-      for (BasicBlock succ : block.getSuccessors()) {
-        depthFirstSorting(visitedBlock, succ, builder);
+  private ImmutableList<BasicBlock> depthFirstSorting() {
+    // Stack marker to denote when all successors of a block have been processed.
+    class BlockMarker {
+      final BasicBlock block;
+      public BlockMarker(BasicBlock block) {
+        this.block = block;
       }
-      builder.add(block);
     }
+    ArrayList<BasicBlock> reverseOrdered = new ArrayList<>(blocks.size());
+    Set<BasicBlock> visitedBlocks = new HashSet<>(blocks.size());
+    Deque<Object> worklist = new ArrayDeque<>(blocks.size());
+    worklist.addLast(blocks.getFirst());
+    while (!worklist.isEmpty()) {
+      Object item = worklist.removeLast();
+      if (item instanceof BlockMarker) {
+        reverseOrdered.add(((BlockMarker) item).block);
+        continue;
+      }
+      BasicBlock block = (BasicBlock) item;
+      if (!visitedBlocks.contains(block)) {
+        visitedBlocks.add(block);
+        worklist.addLast(new BlockMarker(block));
+        for (int i = block.getSuccessors().size() - 1; i >= 0; i--) {
+          worklist.addLast(block.getSuccessors().get(i));
+        }
+      }
+    }
+    ImmutableList.Builder<BasicBlock> builder = ImmutableList.builder();
+    for (int i = reverseOrdered.size() - 1; i >= 0; i--) {
+      builder.add(reverseOrdered.get(i));
+    }
+    return builder.build();
   }
 
   // Reorder the blocks forcing all exceptional blocks to be at the end.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java b/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
index dd7767a..55037e0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
@@ -20,14 +20,16 @@
   public boolean shouldBeEliminated(Instruction instruction) {
     if (instruction.isMove()) {
       Move move = instruction.asMove();
-      int moveSrcRegister = allocator.getRegisterForValue(move.src(), move.getNumber());
+      int moveSrcRegister =
+          allocator.getArgumentOrAllocateRegisterForValue(move.src(), move.getNumber());
       int moveDstRegister = allocator.getRegisterForValue(move.dest(), move.getNumber());
       if (moveSrcRegister == moveDstRegister) {
         return true;
       }
       for (Move activeMove : activeMoves) {
         int activeMoveSrcRegister =
-            allocator.getRegisterForValue(activeMove.src(), activeMove.getNumber());
+            allocator.getArgumentOrAllocateRegisterForValue(
+                activeMove.src(), activeMove.getNumber());
         int activeMoveDstRegister =
             allocator.getRegisterForValue(activeMove.dest(), activeMove.getNumber());
         if (activeMoveSrcRegister == moveSrcRegister && activeMoveDstRegister == moveDstRegister) {
@@ -47,19 +49,21 @@
     if (instruction.outValue() != null && instruction.outValue().needsRegister()) {
       Value defined = instruction.outValue();
       int definedRegister = allocator.getRegisterForValue(defined, instruction.getNumber());
-      activeMoves.removeIf((m) -> {
-        int moveSrcRegister = allocator.getRegisterForValue(m.src(), m.getNumber());
-        int moveDstRegister = allocator.getRegisterForValue(m.dest(), m.getNumber());
-        for (int i = 0; i < defined.requiredRegisters(); i++) {
-          for (int j = 0; j < m.outValue().requiredRegisters(); j++) {
-            if (definedRegister + i == moveDstRegister + j
-                || definedRegister + i == moveSrcRegister + j) {
-              return true;
+      activeMoves.removeIf(
+          (m) -> {
+            int moveSrcRegister =
+                allocator.getArgumentOrAllocateRegisterForValue(m.src(), m.getNumber());
+            int moveDstRegister = allocator.getRegisterForValue(m.dest(), m.getNumber());
+            for (int i = 0; i < defined.requiredRegisters(); i++) {
+              for (int j = 0; j < m.outValue().requiredRegisters(); j++) {
+                if (definedRegister + i == moveDstRegister + j
+                    || definedRegister + i == moveSrcRegister + j) {
+                  return true;
+                }
+              }
             }
-          }
-        }
-        return false;
-      });
+            return false;
+          });
     }
     if (instruction.isMove()) {
       activeMoves.add(instruction.asMove());
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index b01a29a..89f9352 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -1666,13 +1666,10 @@
       usePositions.set(getMoveExceptionRegister(), 0);
     }
 
-    // Treat active linked argument intervals as pinned. They cannot be given another register
-    // at their uses.
-    blockLinkedRegisters(active, unhandledInterval, registerConstraint, usePositions,
-        blockedPositions);
-
-    // Treat inactive linked argument intervals as pinned. They cannot be given another register
-    // at their uses.
+    // Treat active and inactive linked argument intervals as pinned. They cannot be given another
+    // register at their uses.
+    blockLinkedRegisters(
+        active, unhandledInterval, registerConstraint, usePositions, blockedPositions);
     blockLinkedRegisters(inactive, unhandledInterval, registerConstraint, usePositions,
         blockedPositions);
 
@@ -1708,7 +1705,7 @@
     }
 
     int largestUsePosition = getLargestPosition(usePositions, candidate, needsRegisterPair);
-    int blockedPosition = getBlockedPosition(blockedPositions, candidate, needsRegisterPair);
+    int blockedPosition = getLargestPosition(blockedPositions, candidate, needsRegisterPair);
 
     if (largestUsePosition < unhandledInterval.getFirstUse()) {
       // All active and inactive intervals are used before current. Therefore, it is best to spill
@@ -1731,26 +1728,13 @@
     }
   }
 
-  private int getLargestPosition(RegisterPositions usePositions, int register,
-      boolean needsRegisterPair) {
-    int largestUsePosition = usePositions.get(register);
-
+  private int getLargestPosition(
+      RegisterPositions positions, int register, boolean needsRegisterPair) {
+    int position = positions.get(register);
     if (needsRegisterPair) {
-      largestUsePosition = Math.min(largestUsePosition, usePositions.get(register + 1));
+      return Math.min(position, positions.get(register + 1));
     }
-
-    return largestUsePosition;
-  }
-
-  private int getBlockedPosition(RegisterPositions blockedPositions, int register,
-      boolean needsRegisterPair) {
-    int blockedPosition = blockedPositions.get(register);
-
-    if (needsRegisterPair) {
-      blockedPosition = Math.min(blockedPosition, blockedPositions.get(register + 1));
-    }
-
-    return blockedPosition;
+    return position;
   }
 
   private void assignRegisterAndSpill(
@@ -2189,7 +2173,22 @@
             if (options.isGeneratingDex()) {
               int inConstraint = instruction.maxInValueRegister();
               LiveIntervals useIntervals = use.getLiveIntervals();
-              useIntervals.addUse(new LiveIntervalsUse(instruction.getNumber(), inConstraint));
+              // Arguments are always kept in their original, incoming register. For every
+              // unconstrained use of an argument we therefore use its incoming register.
+              // As a result, we do not need to record that the argument is being used at the
+              // current instruction.
+              //
+              // For ranged invoke instructions that use a subset of the arguments in the current
+              // order, registering a use for the arguments at the invoke can cause us to run out of
+              // registers. That is because all arguments are forced back into a chosen register at
+              // all uses. Therefore, if we register a use of an argument where we can actually use
+              // it in the argument register, the register allocator would use two registers for the
+              // argument but in reality only use one.
+              boolean isUnconstrainedArgumentUse =
+                  use.isArgument() && inConstraint == Constants.U16BIT_MAX;
+              if (!isUnconstrainedArgumentUse) {
+                useIntervals.addUse(new LiveIntervalsUse(instruction.getNumber(), inConstraint));
+              }
             }
           }
         }
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 c4a4478..48ea17d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -20,6 +20,7 @@
 
   public static class Builder {
 
+    private final List<String> parsedConfiguration = new ArrayList<>();
     private final List<FilteredClassPath> injars = new ArrayList<>();
     private final List<FilteredClassPath> libraryjars = new ArrayList<>();
     private final Reporter reporter;
@@ -30,6 +31,8 @@
     private boolean optimizing = true;
     private boolean obfuscating = true;
     private boolean shrinking = true;
+    private boolean printConfiguration;
+    private Path printConfigurationFile;
     private boolean printUsage;
     private Path printUsageFile;
     private boolean printMapping;
@@ -51,6 +54,10 @@
     private Origin keepParameterNamesOptionOrigin;
     private Position keepParameterNamesOptionPosition;
     private final ProguardClassFilter.Builder adaptClassStrings = ProguardClassFilter.builder();
+    private final ProguardPathFilter.Builder adaptResourceFilenames = ProguardPathFilter.builder();
+    private final ProguardPathFilter.Builder adaptResourceFilecontents =
+        ProguardPathFilter.builder();
+    private final ProguardPathFilter.Builder keepDirectories = ProguardPathFilter.builder();
     private boolean forceProguardCompatibility = false;
     private boolean overloadAggressively;
 
@@ -59,6 +66,10 @@
       this.reporter = reporter;
     }
 
+    public void addParsedConfiguration(String source) {
+      parsedConfiguration.add(source);
+    }
+
     public void addInjars(List<FilteredClassPath> injars) {
       this.injars.addAll(injars);
     }
@@ -113,6 +124,15 @@
       shrinking = false;
     }
 
+    public void setPrintConfiguration(boolean printConfiguration) {
+      this.printConfiguration = printConfiguration;
+    }
+
+    public void setPrintConfigurationFile(Path file) {
+      assert printConfiguration;
+      this.printConfigurationFile = file;
+    }
+
     public void setPrintUsage(boolean printUsage) {
       this.printUsage = printUsage;
     }
@@ -206,6 +226,18 @@
       adaptClassStrings.addPattern(pattern);
     }
 
+    public void addAdaptResourceFilenames(ProguardPathList pattern) {
+      adaptResourceFilenames.addPattern(pattern);
+    }
+
+    public void addAdaptResourceFilecontents(ProguardPathList pattern) {
+      adaptResourceFilecontents.addPattern(pattern);
+    }
+
+    public void addKeepDirectories(ProguardPathList pattern) {
+      keepDirectories.addPattern(pattern);
+    }
+
     public void setForceProguardCompatibility(boolean forceProguardCompatibility) {
       this.forceProguardCompatibility = forceProguardCompatibility;
     }
@@ -217,6 +249,7 @@
     public ProguardConfiguration buildRaw() {
 
       ProguardConfiguration configuration = new ProguardConfiguration(
+          String.join(System.lineSeparator(), parsedConfiguration),
           dexItemFactory,
           injars,
           libraryjars,
@@ -227,6 +260,8 @@
           optimizing,
           obfuscating,
           shrinking,
+          printConfiguration,
+          printConfigurationFile,
           printUsage,
           printUsageFile,
           printMapping,
@@ -245,7 +280,10 @@
           DictionaryReader.readAllNames(packageObfuscationDictionary, reporter),
           useUniqueClassMemberNames,
           keepParameterNames,
-          adaptClassStrings.build());
+          adaptClassStrings.build(),
+          adaptResourceFilenames.build(),
+          adaptResourceFilecontents.build(),
+          keepDirectories.build());
 
       reporter.failIfPendingErrors();
 
@@ -279,6 +317,7 @@
     }
   }
 
+  private final String parsedConfiguration;
   private final DexItemFactory dexItemFactory;
   private final ImmutableList<FilteredClassPath> injars;
   private final ImmutableList<FilteredClassPath> libraryjars;
@@ -289,6 +328,8 @@
   private final boolean optimizing;
   private final boolean obfuscating;
   private final boolean shrinking;
+  private final boolean printConfiguration;
+  private final Path printConfigurationFile;
   private final boolean printUsage;
   private final Path printUsageFile;
   private final boolean printMapping;
@@ -308,8 +349,12 @@
   private final boolean useUniqueClassMemberNames;
   private final boolean keepParameterNames;
   private final ProguardClassFilter adaptClassStrings;
+  private final ProguardPathFilter adaptResourceFilenames;
+  private final ProguardPathFilter adaptResourceFilecontents;
+  private final ProguardPathFilter keepDirectories;
 
   private ProguardConfiguration(
+      String parsedConfiguration,
       DexItemFactory factory,
       List<FilteredClassPath> injars,
       List<FilteredClassPath> libraryjars,
@@ -320,6 +365,8 @@
       boolean optimizing,
       boolean obfuscating,
       boolean shrinking,
+      boolean printConfiguration,
+      Path printConfigurationFile,
       boolean printUsage,
       Path printUsageFile,
       boolean printMapping,
@@ -338,7 +385,11 @@
       ImmutableList<String> packageObfuscationDictionary,
       boolean useUniqueClassMemberNames,
       boolean keepParameterNames,
-      ProguardClassFilter adaptClassStrings) {
+      ProguardClassFilter adaptClassStrings,
+      ProguardPathFilter adaptResourceFilenames,
+      ProguardPathFilter adaptResourceFilecontents,
+      ProguardPathFilter keepDirectories) {
+    this.parsedConfiguration = parsedConfiguration;
     this.dexItemFactory = factory;
     this.injars = ImmutableList.copyOf(injars);
     this.libraryjars = ImmutableList.copyOf(libraryjars);
@@ -349,6 +400,8 @@
     this.optimizing = optimizing;
     this.obfuscating = obfuscating;
     this.shrinking = shrinking;
+    this.printConfiguration = printConfiguration;
+    this.printConfigurationFile = printConfigurationFile;
     this.printUsage = printUsage;
     this.printUsageFile = printUsageFile;
     this.printMapping = printMapping;
@@ -368,6 +421,9 @@
     this.useUniqueClassMemberNames = useUniqueClassMemberNames;
     this.keepParameterNames = keepParameterNames;
     this.adaptClassStrings = adaptClassStrings;
+    this.adaptResourceFilenames = adaptResourceFilenames;
+    this.adaptResourceFilecontents = adaptResourceFilecontents;
+    this.keepDirectories = keepDirectories;
   }
 
   /**
@@ -378,6 +434,10 @@
     return new Builder(dexItemFactory, reporter);
   }
 
+  public String getParsedConfiguration() {
+    return parsedConfiguration;
+  }
+
   public DexItemFactory getDexItemFactory() {
     return dexItemFactory;
   }
@@ -434,6 +494,14 @@
     return shrinking;
   }
 
+  public boolean isPrintConfiguration() {
+    return printConfiguration;
+  }
+
+  public Path getPrintConfigurationFile() {
+    return printConfigurationFile;
+  }
+
   public boolean isPrintUsage() {
     return printUsage;
   }
@@ -490,6 +558,18 @@
     return adaptClassStrings;
   }
 
+  public ProguardPathFilter getAdaptResourceFilenames() {
+    return adaptResourceFilenames;
+  }
+
+  public ProguardPathFilter getAdaptResourceFilecontents() {
+    return adaptResourceFilecontents;
+  }
+
+  public ProguardPathFilter getKeepDirectories() {
+    return keepDirectories;
+  }
+
   public static ProguardConfiguration defaultConfiguration(DexItemFactory dexItemFactory,
       Reporter reporter) {
     return builder(dexItemFactory, reporter).build();
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 c1af9f4..dd7bb60 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -50,7 +50,6 @@
       "target");
 
   private static final List<String> IGNORED_OPTIONAL_SINGLE_ARG_OPTIONS = ImmutableList.of(
-      "keepdirectories",
       "runtype",
       "laststageoutput");
 
@@ -70,11 +69,9 @@
       "whyarenotsimple");
 
   private static final List<String> WARNED_SINGLE_ARG_OPTIONS = ImmutableList.of(
-      "printconfiguration",
       // TODO -outjars (http://b/37137994) and -adaptresourcefilecontents (http://b/37139570)
       // should be reported as errors, not just as warnings!
-      "outjars",
-      "adaptresourcefilecontents");
+      "outjars");
 
   private static final List<String> WARNED_FLAG_OPTIONS = ImmutableList.of(
       // TODO(b/73707846): add support -addconfigurationdebugging
@@ -165,6 +162,7 @@
     private final String name;
     private final String contents;
     private int position = 0;
+    private int positionAfterInclude = 0;
     private int line = 1;
     private int lineStartPosition = 0;
     private Path baseDirectory;
@@ -181,6 +179,9 @@
       do {
         skipWhitespace();
       } while (parseOption());
+      // Collect the parsed configuration.
+      configurationBuilder.addParsedConfiguration(
+          contents.substring(positionAfterInclude, contents.length()));
     }
 
     private boolean parseOption()
@@ -214,6 +215,13 @@
       } else if (acceptString("checkdiscard")) {
         ProguardCheckDiscardRule rule = parseCheckDiscardRule();
         configurationBuilder.addRule(rule);
+      } else if (acceptString("keepdirectories")) {
+        skipWhitespace();
+        if (isOptionalArgumentGiven()) {
+          configurationBuilder.addKeepDirectories(parsePathFilter());
+        } else {
+          configurationBuilder.addKeepDirectories(ProguardPathList.emptyList());
+        }
       } else if (acceptString("keep")) {
         ProguardKeepRule rule = parseKeepRule();
         configurationBuilder.addRule(rule);
@@ -283,6 +291,12 @@
         configurationBuilder.setOverloadAggressively(true);
       } else if (acceptString("allowaccessmodification")) {
         configurationBuilder.setAllowAccessModification(true);
+      } else if (acceptString("printconfiguration")) {
+        configurationBuilder.setPrintConfiguration(true);
+        skipWhitespace();
+        if (isOptionalArgumentGiven()) {
+          configurationBuilder.setPrintConfigurationFile(parseFileName());
+        }
       } else if (acceptString("printmapping")) {
         configurationBuilder.setPrintMapping(true);
         skipWhitespace();
@@ -298,8 +312,12 @@
         ProguardAssumeValuesRule rule = parseAssumeValuesRule();
         configurationBuilder.addRule(rule);
       } else if (acceptString("include")) {
+        // Collect the parsed configuration until the include.
+        configurationBuilder.addParsedConfiguration(
+            contents.substring(positionAfterInclude, position - ("include".length() + 1)));
         skipWhitespace();
         parseInclude();
+        positionAfterInclude = position;
       } else if (acceptString("basedirectory")) {
         skipWhitespace();
         baseDirectory = parseFileName();
@@ -332,6 +350,20 @@
           configurationBuilder.addAdaptClassStringsPattern(
               ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
         }
+      } else if (acceptString("adaptresourcefilenames")) {
+        skipWhitespace();
+        if (isOptionalArgumentGiven()) {
+          configurationBuilder.addAdaptResourceFilenames(parsePathFilter());
+        } else {
+          configurationBuilder.addAdaptResourceFilenames(ProguardPathList.emptyList());
+        }
+      } else if (acceptString("adaptresourcefilecontents")) {
+        skipWhitespace();
+        if (isOptionalArgumentGiven()) {
+          configurationBuilder.addAdaptResourceFilecontents(parsePathFilter());
+        } else {
+          configurationBuilder.addAdaptResourceFilecontents(ProguardPathList.emptyList());
+        }
       } else if (acceptString("identifiernamestring")) {
         configurationBuilder.addRule(parseIdentifierNameStringRule());
       } else if (acceptString("if")) {
@@ -1279,6 +1311,34 @@
       return name;
     }
 
+    private boolean pathFilterMatcher(Integer character) {
+      return character != ',' && !Character.isWhitespace(character);
+    }
+
+    private ProguardPathList parsePathFilter() throws ProguardRuleParserException {
+      ProguardPathList.Builder builder = ProguardPathList.builder();
+      skipWhitespace();
+      boolean negated = acceptChar('!');
+      String fileFilter = acceptString(this::pathFilterMatcher);
+      if (fileFilter == null) {
+        throw parseError("Path filter expected");
+      }
+      builder.addFileName(negated, fileFilter);
+      skipWhitespace();
+      while (acceptChar(',')) {
+        skipWhitespace();
+        negated = acceptChar('!');
+        skipWhitespace();
+        fileFilter = acceptString(this::pathFilterMatcher);
+        if (fileFilter == null) {
+          throw parseError("Path filter expected");
+        }
+        builder.addFileName(negated, fileFilter);
+        skipWhitespace();
+      }
+      return builder.build();
+    }
+
     private String snippetForPosition() {
       // TODO(ager): really should deal with \r as well to get column right.
       String[] lines = contents.split("\n", -1);  // -1 to get trailing empty lines represented.
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPathFilter.java b/src/main/java/com/android/tools/r8/shaking/ProguardPathFilter.java
new file mode 100644
index 0000000..cb2e117
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPathFilter.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2018, 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.shaking;
+
+import com.google.common.collect.ImmutableList;
+
+public class ProguardPathFilter {
+  private final ImmutableList<ProguardPathList> patterns;
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+    private final ImmutableList.Builder<ProguardPathList> patterns = ImmutableList.builder();
+
+    private Builder() {
+    }
+
+    public Builder addPattern(ProguardPathList pattern) {
+      patterns.add(pattern);
+      return this;
+    }
+
+    ProguardPathFilter build() {
+      return new ProguardPathFilter(patterns.build());
+    }
+  }
+
+  private ProguardPathFilter(ImmutableList<ProguardPathList> patterns) {
+    if (patterns.isEmpty()) {
+      this.patterns = ImmutableList.of(ProguardPathList.emptyList());
+    } else {
+      this.patterns = patterns;
+    }
+  }
+
+  public boolean matches(String path) {
+    for (ProguardPathList pattern : patterns) {
+      if (pattern.matches(path)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java b/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java
new file mode 100644
index 0000000..6766128
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java
@@ -0,0 +1,128 @@
+// Copyright (c) 2018, 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.shaking;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public abstract class ProguardPathList {
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static ProguardPathList emptyList() {
+    return new EmptyPathList();
+  }
+
+  abstract boolean matches(String path);
+
+  public static class Builder {
+
+    private final ImmutableList.Builder<FileNameMatcher> matchers = ImmutableList.builder();
+
+    private Builder() {
+    }
+
+    public Builder addFileName(boolean isNegated, String path) {
+      matchers.add(new FileNameMatcher(isNegated, path));
+      return this;
+    }
+
+    ProguardPathList build() {
+      List<FileNameMatcher> matchers = this.matchers.build();
+      if (matchers. size() > 0) {
+        return new PathList(matchers);
+      } else {
+        return emptyList();
+      }
+    }
+  }
+
+  private static class FileNameMatcher {
+    public final boolean negated;
+    public final String pattern;
+
+    FileNameMatcher(boolean negated, String pattern) {
+      this.negated = negated;
+      this.pattern = pattern;
+    }
+
+    private boolean match(String path) {
+      return matchImpl(pattern, 0, path, 0);
+    }
+
+    private boolean matchImpl(String pattern, int patternIndex, String path, int pathIndex) {
+      for (int i = patternIndex; i < pattern.length(); i++) {
+        char patternChar = pattern.charAt(i);
+        switch (patternChar) {
+          case '*':
+            boolean includeSeparators = pattern.length() > (i + 1) && pattern.charAt(i + 1) == '*';
+            int nextPatternIndex = i + (includeSeparators ? 2 : 1);
+            // Fast cases for the common case where a pattern ends with '**' or '*'.
+            if (nextPatternIndex == pattern.length()) {
+              return includeSeparators || !containsSeparatorsStartingAt(path, pathIndex);
+            }
+            // Match the rest of the pattern against the (non-empty) rest of the class name.
+            for (int nextPathIndex = pathIndex; nextPathIndex < path.length(); nextPathIndex++) {
+              if (!includeSeparators && path.charAt(nextPathIndex) == '/') {
+                return matchImpl(pattern, nextPatternIndex, path, nextPathIndex);
+              }
+              if (matchImpl(pattern, nextPatternIndex, path, nextPathIndex)) {
+                return true;
+              }
+            }
+            break;
+          case '?':
+            if (pathIndex == path.length() || path.charAt(pathIndex++) == '/') {
+              return false;
+            }
+            break;
+          default:
+            if (pathIndex == path.length() || patternChar != path.charAt(pathIndex++)) {
+              return false;
+            }
+            break;
+        }
+      }
+      return pathIndex == path.length();
+    }
+
+    private boolean containsSeparatorsStartingAt(String path, int pathIndex) {
+      return path.indexOf('/', pathIndex) != -1;
+    }
+
+  }
+
+  private static class PathList extends ProguardPathList {
+    private final List<FileNameMatcher> matchers;
+
+    private PathList(List<FileNameMatcher> matchers) {
+      this.matchers = matchers;
+    }
+
+    @Override
+    boolean matches(String path) {
+      for (FileNameMatcher matcher : matchers) {
+        if (matcher.match(path)) {
+          // If we match a negation, abort as non-match. If we match a positive, return true.
+          return !matcher.negated;
+        }
+      }
+      return false;
+    }
+  }
+
+  private static class EmptyPathList extends ProguardPathList {
+
+    private EmptyPathList() {
+    }
+
+    @Override
+    boolean matches(String path) {
+      return true;
+    }
+  }
+}
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 42775d2..04c5f72 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -251,6 +251,10 @@
   // If non-null, it must be and is passed to the consumer.
   public StringConsumer usageInformationConsumer = null;
 
+  // If null, no configuration information needs to be printed.
+  // If non-null, configuration must be passed to the consumer.
+  public StringConsumer configurationConsumer = null;
+
   public Path proguardCompatibilityRulesOutput = null;
 
   public void warningMissingEnclosingMember(DexType clazz, Origin origin, int version) {
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 5d7bec0..a89cbd4 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -373,6 +373,17 @@
   }
 
   @Test
+  public void printsConfigurationOnStdout() throws Throwable {
+    Path proguardPrintConfigurationConfiguration =
+        temp.newFile("printconfiguration.txt").toPath().toAbsolutePath();
+    FileUtils.writeTextFile(
+        proguardPrintConfigurationConfiguration, ImmutableList.of("-printconfiguration"));
+    ProcessResult result = runR8OnShaking1(proguardPrintConfigurationConfiguration);
+    assertEquals("R8 run failed: " + result.stderr, 0, result.exitCode);
+    assertTrue(result.stdout.contains("-printconfiguration"));
+  }
+
+  @Test
   public void printsPrintSeedsOnStdout() throws Throwable {
     Path proguardPrintSeedsConfiguration = temp.newFile("printseeds.txt").toPath().toAbsolutePath();
     FileUtils.writeTextFile(proguardPrintSeedsConfiguration, ImmutableList.of("-printseeds"));
@@ -402,6 +413,21 @@
     assertTrue(result.stdout.contains("shaking1.Unused"));
   }
 
+  @Test
+  public void printsPrintSeedsAndPrintUsageAndPrintConfigurationOnStdout() throws Throwable {
+    Path proguardPrintSeedsConfiguration =
+        temp.newFile("printseedsandprintusageandprintconfiguration.txt").toPath().toAbsolutePath();
+    FileUtils.writeTextFile(proguardPrintSeedsConfiguration,
+        ImmutableList.of("-printseeds", "-printusage", "-printconfiguration"));
+    ProcessResult result = runR8OnShaking1(proguardPrintSeedsConfiguration);
+    assertEquals("R8 run failed: " + result.stderr, 0, result.exitCode);
+    assertTrue(result.stdout.contains("void main(java.lang.String[])"));
+    assertTrue(result.stdout.contains("shaking1.Unused"));
+    assertTrue(result.stdout.contains("-printseeds"));
+    assertTrue(result.stdout.contains("-printusage"));
+    assertTrue(result.stdout.contains("-printconfiguration"));
+  }
+
   private R8Command parse(String... args) throws CompilationFailedException {
     return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
   }
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java b/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
index 440f4f3..28a6fa3 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
@@ -16,7 +16,6 @@
 import org.junit.Test;
 
 public class B77240639 extends TestBase {
-  @Ignore("b/77240639")
   @Test
   public void test() throws Exception {
     AndroidApp app = compileWithD8(readClasses(TestClass.class));
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java b/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java
new file mode 100644
index 0000000..7621522
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2018, 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.shaking;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import org.junit.Test;
+
+class PrintConfigurationTestClass {
+
+  public static void main(String[] args) {
+
+  }
+}
+
+public class PrintConfigurationTest extends TestBase {
+  @Test
+  public void testSingleCOnfiguration() throws Exception {
+    Class mainClass = PrintConfigurationTestClass.class;
+    String proguardConfig = keepMainProguardConfiguration(mainClass);
+    Path printConfigurationFile = temp.newFile().toPath();
+    proguardConfig += "\n-printconfiguration " + printConfigurationFile.toString();
+    compileWithR8(ImmutableList.of(mainClass), proguardConfig);
+    assertEquals(proguardConfig, FileUtils.readTextFile(printConfigurationFile, Charsets.UTF_8));
+  }
+
+  @Test
+  public void testIncludeFile() throws Exception {
+    Class mainClass = PrintConfigurationTestClass.class;
+    String includeProguardConfig = keepMainProguardConfiguration(mainClass);
+    Path includeFile = temp.newFile().toPath();
+    FileUtils.writeTextFile(includeFile, includeProguardConfig);
+    Path printConfigurationFile = temp.newFile().toPath();
+    String proguardConfig = String.join(System.lineSeparator(), ImmutableList.of(
+        "-include " + includeFile.toString(),
+        "-printconfiguration " + printConfigurationFile.toString()
+    ));
+
+    String expected = String.join(System.lineSeparator(), ImmutableList.of(
+        "",  // The -include line turns into an empty line.
+        includeProguardConfig,
+        "",  // Writing to the file adds an ending line separator
+        "",  // An empty line is emitted between two parts
+        "-printconfiguration " + printConfigurationFile.toString()
+    ));
+    compileWithR8(ImmutableList.of(mainClass), proguardConfig);
+    assertEquals(expected, FileUtils.readTextFile(printConfigurationFile, Charsets.UTF_8));
+  }
+}
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 5dc4ec6..9e9f80e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.position.TextPosition;
 import com.android.tools.r8.position.TextRange;
@@ -157,11 +158,13 @@
 
   private Reporter reporter;
   private KeepingDiagnosticHandler handler;
+  private ProguardConfigurationParser parser;
 
   @Before
-  public void setup() {
+  public void reset() {
     handler = new KeepingDiagnosticHandler();
     reporter = new Reporter(handler);
+    parser = new ProguardConfigurationParser(new DexItemFactory(), reporter);
   }
 
   @Test
@@ -944,6 +947,32 @@
   }
 
   @Test
+  public void parse_printconfiguration_noArguments() throws Exception {
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), reporter);
+    parser.parse(createConfigurationForTesting(ImmutableList.of(
+        "-printconfiguration"
+    )));
+    verifyParserEndsCleanly();
+    ProguardConfiguration config = parser.getConfig();
+    assertTrue(config.isPrintConfiguration());
+    assertNull(config.getPrintConfigurationFile());
+  }
+
+  @Test
+  public void parse_printconfiguration_argument() throws Exception {
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), reporter);
+    parser.parse(createConfigurationForTesting(ImmutableList.of(
+        "-printconfiguration file_name"
+    )));
+    verifyParserEndsCleanly();
+    ProguardConfiguration config = parser.getConfig();
+    assertTrue(config.isPrintConfiguration());
+    assertEquals("." + File.separator + "file_name", config.getPrintConfigurationFile().toString());
+  }
+
+  @Test
   public void parsePrintUsage() throws Exception {
     ProguardConfigurationParser parser =
         new ProguardConfigurationParser(new DexItemFactory(), reporter);
@@ -1149,6 +1178,146 @@
     }
   }
 
+  private void checkFileFilterMatchAnything(ProguardPathFilter filter) {
+    assertTrue(filter.matches("x"));
+    assertTrue(filter.matches("x/y"));
+    assertTrue(filter.matches("x/y/x"));
+  }
+
+  @Test
+  public void parse_adaptresourcexxx_keepdirectories_noArguments1() {
+    ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
+        "-adaptresourcefilenames",
+        "-adaptresourcefilecontents",
+        "-keepdirectories"
+    ));
+    checkFileFilterMatchAnything(config.getAdaptResourceFilenames());
+    checkFileFilterMatchAnything(config.getAdaptResourceFilecontents());
+    checkFileFilterMatchAnything(config.getKeepDirectories());
+  }
+
+  @Test
+  public void parse_adaptresourcexxx_keepdirectories_noArguments2() {
+    ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
+        "-keepdirectories",
+        "-adaptresourcefilenames",
+        "-adaptresourcefilecontents"
+    ));
+    checkFileFilterMatchAnything(config.getAdaptResourceFilenames());
+    checkFileFilterMatchAnything(config.getAdaptResourceFilecontents());
+    checkFileFilterMatchAnything(config.getKeepDirectories());
+  }
+
+  @Test
+  public void parse_adaptresourcexxx_keepdirectories_noArguments3() {
+    ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
+        "-adaptresourcefilecontents",
+        "-keepdirectories",
+        "-adaptresourcefilenames"
+    ));
+    checkFileFilterMatchAnything(config.getAdaptResourceFilenames());
+    checkFileFilterMatchAnything(config.getAdaptResourceFilecontents());
+    checkFileFilterMatchAnything(config.getKeepDirectories());
+  }
+
+  private String FILE_FILTER_SINGLE = "xxx/*";
+
+  private void checkFileFilterSingle(ProguardPathFilter filter) {
+    assertTrue(filter.matches("xxx/x"));
+    assertTrue(filter.matches("xxx/"));
+    assertFalse(filter.matches("xxx/yyy/z"));
+    assertFalse(filter.matches("xxx"));
+  }
+
+  @Test
+  public void parse_adaptresourcexxx_keepdirectories_singleArgument() {
+    ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
+        "-adaptresourcefilenames " + FILE_FILTER_SINGLE,
+        "-adaptresourcefilecontents " + FILE_FILTER_SINGLE,
+        "-keepdirectories " + FILE_FILTER_SINGLE
+    ));
+    checkFileFilterSingle(config.getAdaptResourceFilenames());
+    checkFileFilterSingle(config.getAdaptResourceFilecontents());
+    checkFileFilterSingle(config.getKeepDirectories());
+  }
+
+  private String FILE_FILTER_MULTIPLE =
+      "xxx/*, !**.gif  ,images/**  ,  com/myapp/**/*.xml,com/mylib/*/*.xml";
+
+  private void checkFileFilterMultiple(ProguardPathFilter filter) {
+    assertTrue(filter.matches("xxx/x"));
+    assertTrue(filter.matches("xxx/x.gif"));
+    assertTrue(filter.matches("images/x.jpg"));
+    assertTrue(filter.matches("images/xxx/x.jpg"));
+    assertTrue(filter.matches("com/myapp/package1/x.xml"));
+    assertTrue(filter.matches("com/myapp/package1/package2/x.xml"));
+    assertTrue(filter.matches("com/mylib/package1/x.xml"));
+    assertFalse(filter.matches("x.gif"));
+    assertFalse(filter.matches("images/x.gif"));
+    assertFalse(filter.matches("images/xxx/y.gif"));
+    assertFalse(filter.matches("images/xxx/yyy/z.gif"));
+    assertFalse(filter.matches("com/myapp/package1/x.jpg"));
+    assertFalse(filter.matches("com/myapp/package1/package2/x.jpg"));
+    assertFalse(filter.matches("com/mylib/package1/package2/x.xml"));
+  }
+
+  @Test
+  public void parse_adaptresourcexxx_keepdirectories_multipleArgument() {
+    ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
+        "-adaptresourcefilenames " + FILE_FILTER_MULTIPLE,
+        "-adaptresourcefilecontents " + FILE_FILTER_MULTIPLE,
+        "-keepdirectories " + FILE_FILTER_MULTIPLE
+    ));
+    checkFileFilterMultiple(config.getAdaptResourceFilenames());
+    checkFileFilterMultiple(config.getAdaptResourceFilecontents());
+    checkFileFilterMultiple(config.getKeepDirectories());
+  }
+
+  @Test
+  public void parse_adaptresourcexxx_keepdirectories_leadingComma() {
+    List<String> options = ImmutableList.of(
+        "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories");
+    for (String option : options) {
+      try {
+        reset();
+        parser.parse(createConfigurationForTesting(ImmutableList.of(option + " ,")));
+        fail("Expect to fail due to the lack of path filter.");
+      } catch (AbortException e) {
+        checkDiagnostic(handler.errors, null, 1, option.length() + 2, "Path filter expected");
+      }
+    }
+  }
+
+  @Test
+  public void parse_adaptresourcexxx_keepdirectories_emptyListElement() {
+    List<String> options = ImmutableList.of(
+        "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories");
+    for (String option : options) {
+      try {
+        reset();
+        parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,,yyy")));
+        fail("Expect to fail due to the lack of path filter.");
+      } catch (AbortException e) {
+        checkDiagnostic(handler.errors, null, 1, option.length() + 6, "Path filter expected");
+      }
+    }
+  }
+
+  @Test
+  public void parse_adaptresourcexxx_keepdirectories_trailingComma() {
+    List<String> options = ImmutableList.of(
+        "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories");
+    for (String option : options) {
+      try {
+        reset();
+        parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,")));
+        fail("Expect to fail due to the lack of path filter.");
+      } catch (AbortException e) {
+        checkDiagnostic(handler.errors, null, 1, option.length() + 6, "Path filter expected");
+      }
+    }
+  }
+
   @Test
   public void parse_if() throws Exception {
     Path proguardConfig = writeTextToTempFile(
@@ -1433,6 +1602,12 @@
     verifyWithProguard(proguardConfig);
   }
 
+  private ProguardConfiguration parseAndVerifyParserEndsCleanly(List<String> config) {
+    parser.parse(createConfigurationForTesting(config));
+    verifyParserEndsCleanly();
+    return parser.getConfig();
+  }
+
   private void verifyParserEndsCleanly() {
     assertEquals(0, handler.infos.size());
     assertEquals(0, handler.warnings.size());
@@ -1443,7 +1618,11 @@
       int columnStart, String... messageParts) {
     assertEquals(1, diagnostics.size());
     Diagnostic diagnostic = diagnostics.get(0);
-    assertEquals(path, ((PathOrigin) diagnostic.getOrigin()).getPath());
+    if (path != null) {
+      assertEquals(path, ((PathOrigin) diagnostic.getOrigin()).getPath());
+    } else {
+      assertSame(Origin.unknown(), diagnostic.getOrigin());
+    }
     TextPosition position;
     if (diagnostic.getPosition() instanceof TextRange) {
       position = ((TextRange) diagnostic.getPosition()).getStart();
diff --git a/tools/test_gradle_benchmarks.py b/tools/test_gradle_benchmarks.py
index 6720c4a..189b9da 100755
--- a/tools/test_gradle_benchmarks.py
+++ b/tools/test_gradle_benchmarks.py
@@ -3,7 +3,6 @@
 # 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.
 
-
 from __future__ import print_function
 import argparse
 import os
@@ -12,7 +11,6 @@
 import gradle
 from enum import Enum
 
-
 BENCHMARKS_ROOT_DIR = os.path.join(utils.REPO_ROOT, 'third_party', 'benchmarks')
 
 def parse_arguments():
@@ -24,9 +22,10 @@
                       choices=['dx', 'd8'],
                       required=True,
                       help='Compiler tool to use.')
+  parser.add_argument('--benchmark',
+                      help='Which benchmark to run, default all')
   return parser.parse_args()
 
-
 class Benchmark:
   class Tools(Enum):
     D8 = 1
@@ -88,7 +87,6 @@
   def EnsurePresence(self):
     EnsurePresence(self.rootDirPath, self.displayName)
 
-
 def EnsurePresence(dir, displayName):
   if not os.path.exists(dir) or os.path.getmtime(dir + '.tar.gz')\
           < os.path.getmtime(dir + '.tar.gz.sha1'):
@@ -111,7 +109,6 @@
 
   return any(namePattern in taskname for namePattern in acceptedGradleTasks)
 
-
 def PrintBuildTimeForGolem(benchmark, stdOut):
   for line in stdOut.splitlines():
     if 'BENCH' in line and benchmark.moduleName in line:
@@ -138,7 +135,6 @@
         print('{}(RunTimeRaw): {} ms'
               .format(benchmark.displayName + '-' + taskName, commaSplit[2]))
 
-
 def Main():
   args = parse_arguments()
 
@@ -191,10 +187,16 @@
 
   ]
 
-  EnsurePresence(os.path.join('third_party', 'benchmarks', 'android-sdk'), 'android SDK')
-  EnsurePresence(os.path.join('third_party', 'gradle-plugin'), 'Android Gradle plugin')
-
-  for benchmark in buildTimeBenchmarks:
+  EnsurePresence(os.path.join('third_party', 'benchmarks', 'android-sdk'),
+                 'android SDK')
+  EnsurePresence(os.path.join('third_party', 'gradle-plugin'),
+                 'Android Gradle plugin')
+  toRun = buildTimeBenchmarks
+  if args.benchmark:
+    toRun = [b for b in toRun if b.displayName == args.benchmark]
+    if len(toRun) != 1:
+      raise AssertionError("Unknown benchmark: " + args.benchmark)
+  for benchmark in toRun:
     benchmark.EnsurePresence()
     benchmark.Clean()
     stdOut = benchmark.Build(tool, desugarMode)