Merge "Fix warnings related to ASM5 deprecated method"
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 21ed16e..7e2e851 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
@@ -42,6 +42,41 @@
     this.valueNumberGenerator = valueNumberGenerator;
   }
 
+  /**
+   * Trace blocks and attempt to put fallthrough blocks immediately after the block that
+   * falls through. When we fail to do that we create a new fallthrough block with an explicit
+   * goto to the actual fallthrough block.
+   */
+  public void traceBlocks() {
+    BasicBlock[] sorted = topologicallySortedBlocks();
+    clearMarks();
+    int nextBlockNumber = blocks.size();
+    LinkedList<BasicBlock> tracedBlocks = new LinkedList<>();
+    for (BasicBlock block : sorted) {
+      if (!block.isMarked()) {
+        block.mark();
+        tracedBlocks.add(block);
+        BasicBlock current = block;
+        BasicBlock fallthrough = block.exit().fallthroughBlock();
+        while (fallthrough != null && !fallthrough.isMarked()) {
+          fallthrough.mark();
+          tracedBlocks.add(fallthrough);
+          current = fallthrough;
+          fallthrough = fallthrough.exit().fallthroughBlock();
+        }
+        if (fallthrough != null) {
+          BasicBlock newFallthrough = BasicBlock.createGotoBlock(fallthrough, nextBlockNumber++);
+          current.exit().setFallthroughBlock(newFallthrough);
+          newFallthrough.getPredecessors().add(current);
+          fallthrough.replacePredecessor(current, newFallthrough);
+          newFallthrough.mark();
+          tracedBlocks.add(newFallthrough);
+        }
+      }
+    }
+    blocks = tracedBlocks;
+  }
+
   private void ensureBlockNumbering() {
     if (!numbered) {
       numbered = true;
@@ -327,6 +362,10 @@
     return normalExitBlock;
   }
 
+  public void invalidateNormalExitBlock() {
+    normalExitBlock = null;
+  }
+
   public ListIterator<BasicBlock> listIterator() {
     return new BasicBlockIterator(this);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 28a4d74..a644470 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -363,4 +363,8 @@
   public Set<Value> getDebugValues() {
     return debugValues != null ? debugValues : ImmutableSet.of();
   }
+
+  public boolean usesValueOneTime(Value usedValue) {
+    return operands.indexOf(usedValue) == operands.lastIndexOf(usedValue);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index 337ed71..ba2fcb0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -67,7 +67,7 @@
 
   @Override
   public void buildDex(DexBuilder builder) {
-    builder.add(this, createDexInstruction(builder));
+    builder.addReturn(this, createDexInstruction(builder));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index cbd8941..8540bf3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -402,6 +402,33 @@
     add(argument, new FallThroughInfo(argument));
   }
 
+  public void addReturn(Return ret, Instruction dex) {
+    if (nextBlock != null) {
+      Return followingRet = nextBlock.exit().asReturn();
+      if (nextBlock.getInstructions().size() == 1
+          && followingRet != null
+          && ret.getReturnType() == followingRet.getReturnType()) {
+        if (ret.isReturnVoid() && followingRet.isReturnVoid()) {
+          addNop(ret);
+          return;
+        }
+        if (!ret.isReturnVoid()
+            && !followingRet.isReturnVoid()
+            && ret.returnValue().outType() == followingRet.returnValue().outType()) {
+          int thisRegister = registerAllocator.getRegisterForValue(
+              ret.returnValue(), ret.getNumber());
+          int otherRegister = registerAllocator.getRegisterForValue(
+              followingRet.returnValue(), followingRet.getNumber());
+          if (thisRegister == otherRegister) {
+            addNop(ret);
+            return;
+          }
+        }
+      }
+    }
+    add(ret, dex);
+  }
+
   private void add(com.android.tools.r8.ir.code.Instruction ir, Info info) {
     assert ir != null;
     assert info != null;
@@ -436,10 +463,24 @@
         return info;
       }
     }
-    assert instruction != null && instruction.isGoto();
+    assert instruction != null;
+    if (instruction.isReturn()) {
+      assert getInfo(instruction) instanceof FallThroughInfo;
+      return getTargetInfo(computeNextBlock(block));
+    }
+    assert instruction.isGoto();
     return getTargetInfo(instruction.asGoto().getTarget());
   }
 
+  private BasicBlock computeNextBlock(BasicBlock block) {
+    ListIterator<BasicBlock> it = ir.listIterator();
+    BasicBlock current = it.next();
+    while (current != block) {
+      current = it.next();
+    }
+    return it.next();
+  }
+
   // Helper for computing switch payloads.
   private Nop createSwitchPayload(SwitchPayloadInfo info, int offset) {
     Switch ir = info.ir;
@@ -844,8 +885,10 @@
         // Set the size to the min of the size of the return and the size of the goto. When
         // adding instructions, we use the return if the computed size matches the size of the
         // return.
+        assert !(targetInfo instanceof FallThroughInfo);
         size = Math.min(targetInfo.getSize(), size);
       }
+      assert size != 0;
       return size;
     }
 
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 1b7a276..77f1a16 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
@@ -399,7 +399,7 @@
 
     // Create block order and make sure that all blocks are immediately followed by their
     // fallthrough block if any.
-    traceBlocks(ir);
+    ir.traceBlocks();
 
     // Clear the code so we don't build multiple times.
     source.clear();
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 0ddd43a..8e5b26f 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
@@ -522,6 +522,9 @@
     }
 
     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 fbde02b..5c1114b 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,6 +87,7 @@
 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;
@@ -1177,7 +1178,16 @@
         // needs to go in the immediate dominator block so that it is available for phi moves.
         for (Phi phi : instruction.outValue().uniquePhiUsers()) {
           if (phi.getBlock() == dominator) {
-            dominator = dominatorTree.immediateDominator(dominator);
+            if (instruction.outValue().numberOfAllUsers() == 1 &&
+                  phi.usesValueOneTime(instruction.outValue())) {
+              // Out value is used only one time, move the constant directly to the corresponding
+              // branch rather than into the dominator to avoid to generate a const on paths which
+              // does not required it.
+              int predIndex = phi.getOperands().indexOf(instruction.outValue());
+              dominator = dominator.getPredecessors().get(predIndex);
+            } else {
+              dominator = dominatorTree.immediateDominator(dominator);
+            }
             break;
           }
         }
@@ -1466,6 +1476,87 @@
     }
   }
 
+  /**
+   * 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.getLocalInfo() != null;
+          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/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 2aa9015..a8b3972 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
@@ -369,10 +369,10 @@
           }
         }
         spillCount = 0;
-        if (localsChanged && shouldEmitChangesAtInstruction(instruction)) {
+        if (localsChanged && instruction.getBlock().exit() != instruction) {
           DebugLocalsChange change = createLocalsChange(ending, starting);
           if (change != null) {
-            if (instruction.isDebugPosition() || instruction.isJumpInstruction()) {
+            if (instruction.isDebugPosition()) {
               instructionIterator.previous();
               instructionIterator.add(change);
               instructionIterator.next();
@@ -505,14 +505,6 @@
     return new DebugLocalsChange(ending, starting);
   }
 
-  private boolean shouldEmitChangesAtInstruction(Instruction instruction) {
-    BasicBlock block = instruction.getBlock();
-    // We emit local changes on all non-exit instructions or, since we have only a singe return
-    // block, any exits directly targeting that.
-    return instruction != block.exit()
-        || (instruction.isGoto() && instruction.asGoto().getTarget() == code.getNormalExitBlock());
-  }
-
   private void clearState() {
     liveAtEntrySets = null;
     liveIntervals = null;
@@ -1764,8 +1756,7 @@
     for (LiveIntervals intervals : liveIntervals) {
       if (intervals.hasSplits()) {
         LiveIntervals current = intervals;
-        PriorityQueue<LiveIntervals> sortedChildren =
-            new PriorityQueue<>((o1, o2) -> Integer.compare(o1.getStart(), o2.getStart()));
+        PriorityQueue<LiveIntervals> sortedChildren = new PriorityQueue<>();
         sortedChildren.addAll(current.getSplitChildren());
         for (LiveIntervals split = sortedChildren.poll();
             split != null;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
index e404f51..8869715 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
@@ -27,7 +27,7 @@
       DexAccessFlags negatedClassAccessFlags,
       boolean classTypeNegated,
       ProguardClassType classType,
-      List<ProguardTypeMatcher> classNames,
+      ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
index cc54a49..c7d9ca3 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -26,7 +26,7 @@
       DexAccessFlags negatedClassAccessFlags,
       boolean classTypeNegated,
       ProguardClassType classType,
-      List<ProguardTypeMatcher> classNames,
+      ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
index 73825f1..70f18f7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -25,7 +25,7 @@
       DexAccessFlags negatedClassAccessFlags,
       boolean classTypeNegated,
       ProguardClassType classType,
-      List<ProguardTypeMatcher> classNames,
+      ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
index 54302db..20d1070 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -27,7 +27,7 @@
       DexAccessFlags negatedClassAccessFlags,
       boolean classTypeNegated,
       ProguardClassType classType,
-      List<ProguardTypeMatcher> classNames,
+      ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
new file mode 100644
index 0000000..cd1c5fc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -0,0 +1,186 @@
+// 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.shaking;
+
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap.Entry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public abstract class ProguardClassNameList {
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static ProguardClassNameList singletonList(ProguardTypeMatcher matcher) {
+    return new SingleClassNameList(matcher);
+  }
+
+  public abstract int size();
+
+  public static class Builder {
+
+    /**
+     * Map used to store pairs of patterns and whether they are negated.
+     */
+    private Object2BooleanMap<ProguardTypeMatcher> matchers = new Object2BooleanArrayMap<>();
+
+    private Builder() {
+    }
+
+    public Builder addClassName(boolean isNegated, ProguardTypeMatcher className) {
+      matchers.put(className, isNegated);
+      return this;
+    }
+
+    ProguardClassNameList build() {
+      if (matchers.containsValue(true)) {
+        // At least one pattern is negated.
+        return new MixedClassNameList(matchers);
+      } else {
+        if (matchers.size() == 1) {
+          return new SingleClassNameList(Iterables.getOnlyElement(matchers.keySet()));
+        } else {
+          return new PositiveClassNameList(matchers.keySet());
+        }
+      }
+    }
+
+  }
+
+  public abstract void writeTo(StringBuilder builder);
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    writeTo(builder);
+    return builder.toString();
+  }
+
+  public abstract List<DexType> asSpecificDexTypes();
+
+  public abstract boolean matches(DexType type);
+
+  private static class SingleClassNameList extends ProguardClassNameList {
+
+    private final ProguardTypeMatcher className;
+
+    private SingleClassNameList(ProguardTypeMatcher className) {
+      this.className = className;
+    }
+
+    @Override
+    public int size() {
+      return 1;
+    }
+
+    @Override
+    public void writeTo(StringBuilder builder) {
+      builder.append(className.toString());
+    }
+
+    @Override
+    public List<DexType> asSpecificDexTypes() {
+      DexType specific = className.getSpecificType();
+      return specific == null ? null : Collections.singletonList(specific);
+    }
+
+    @Override
+    public boolean matches(DexType type) {
+      return className.matches(type);
+    }
+  }
+
+  private static class PositiveClassNameList extends ProguardClassNameList {
+
+    private final ImmutableList<ProguardTypeMatcher> classNames;
+
+    private PositiveClassNameList(Collection<ProguardTypeMatcher> classNames) {
+      this.classNames = ImmutableList.copyOf(classNames);
+    }
+
+    @Override
+    public int size() {
+      return classNames.size();
+    }
+
+    @Override
+    public void writeTo(StringBuilder builder) {
+      boolean first = true;
+      for (ProguardTypeMatcher className : classNames) {
+        if (!first) {
+          builder.append(',');
+        }
+        builder.append(className);
+        first = false;
+      }
+    }
+
+    @Override
+    public List<DexType> asSpecificDexTypes() {
+      if (classNames.stream().allMatch(k -> k.getSpecificType() != null)) {
+        return classNames.stream().map(ProguardTypeMatcher::getSpecificType)
+            .collect(Collectors.toList());
+      }
+      return null;
+    }
+
+    @Override
+    public boolean matches(DexType type) {
+      return classNames.stream().anyMatch(name -> name.matches(type));
+    }
+  }
+
+  private static class MixedClassNameList extends ProguardClassNameList {
+
+    private final Object2BooleanMap<ProguardTypeMatcher> classNames;
+
+    private MixedClassNameList(Object2BooleanMap<ProguardTypeMatcher> classNames) {
+      this.classNames = classNames;
+    }
+
+    @Override
+    public int size() {
+      return classNames.size();
+    }
+
+    @Override
+    public void writeTo(StringBuilder builder) {
+      boolean first = true;
+      for (Entry<ProguardTypeMatcher> className : classNames.object2BooleanEntrySet()) {
+        if (!first) {
+          builder.append(',');
+        }
+        if (className.getBooleanValue()) {
+          builder.append('!');
+        }
+        builder.append(className.getKey().toString());
+        first = false;
+      }
+    }
+
+    @Override
+    public List<DexType> asSpecificDexTypes() {
+      return null;
+    }
+
+    @Override
+    public boolean matches(DexType type) {
+      for (Entry<ProguardTypeMatcher> className : classNames.object2BooleanEntrySet()) {
+        if (className.getKey().matches(type)) {
+          // If we match a negation, abort as non-match. If we match a positive, return true.
+          return !className.getBooleanValue();
+        }
+      }
+      return false;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index 396d37d..745bcf1 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -20,7 +20,7 @@
     protected DexAccessFlags negatedClassAccessFlags = new DexAccessFlags(0);
     protected boolean classTypeNegated = false;
     protected ProguardClassType classType;
-    protected List<ProguardTypeMatcher> classNames;
+    protected ProguardClassNameList classNames;
     protected ProguardTypeMatcher inheritanceAnnotation;
     protected ProguardTypeMatcher inheritanceClassName;
     protected boolean inheritanceIsExtends = false;
@@ -65,11 +65,11 @@
       this.inheritanceAnnotation = inheritanceAnnotation;
     }
 
-    public List<ProguardTypeMatcher> getClassNames() {
+    public ProguardClassNameList getClassNames() {
       return classNames;
     }
 
-    public void setClassNames(List<ProguardTypeMatcher> classNames) {
+    public void setClassNames(ProguardClassNameList classNames) {
       this.classNames = classNames;
     }
 
@@ -114,7 +114,7 @@
     }
 
     protected void matchAllSpecification() {
-      setClassNames(Collections.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
+      setClassNames(ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
       setMemberRules(Collections.singleton(ProguardMemberRule.defaultKeepAllRule()));
     }
   }
@@ -124,7 +124,7 @@
   private final DexAccessFlags negatedClassAccessFlags;
   private final boolean classTypeNegated;
   private final ProguardClassType classType;
-  private final List<ProguardTypeMatcher> classNames;
+  private final ProguardClassNameList classNames;
   private final ProguardTypeMatcher inheritanceAnnotation;
   private final ProguardTypeMatcher inheritanceClassName;
   private final boolean inheritanceIsExtends;
@@ -136,7 +136,7 @@
       DexAccessFlags negatedClassAccessFlags,
       boolean classTypeNegated,
       ProguardClassType classType,
-      List<ProguardTypeMatcher> classNames,
+      ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
@@ -173,7 +173,7 @@
     return inheritanceAnnotation;
   }
 
-  public List<ProguardTypeMatcher> getClassNames() {
+  public ProguardClassNameList getClassNames() {
     return classNames;
   }
 
@@ -262,14 +262,7 @@
     }
     builder.append(classType);
     builder.append(' ');
-    boolean first = true;
-    for (ProguardTypeMatcher className : classNames) {
-      builder.append(className);
-      if (!first) {
-        builder.append(',');
-      }
-      first = false;
-    }
+    classNames.writeTo(builder);
     if (hasInheritanceClassName()) {
       builder.append(inheritanceIsExtends ? " extends" : " implements");
       StringUtils.appendNonEmpty(builder, " @", inheritanceAnnotation, null);
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 22fdf45..1c3051d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -132,9 +132,9 @@
           || parseOptimizationOption()) {
         // Intentionally left empty.
       } else if (
-             (option = Iterables.find(warnedSingleArgOptions,
-                 this::skipOptionWithSingleArg, null)) != null
-          || (option = Iterables.find(warnedFlagOptions, this::skipFlag, null)) != null) {
+          (option = Iterables.find(warnedSingleArgOptions,
+              this::skipOptionWithSingleArg, null)) != null
+              || (option = Iterables.find(warnedFlagOptions, this::skipFlag, null)) != null) {
         warnIgnoringOptions(option);
       } else if ((option = Iterables.find(unsupportedFlagOptions, this::skipFlag, null)) != null) {
         throw parseError("Unsupported option: -" + option);
@@ -515,7 +515,7 @@
         } else {
           DexAccessFlags flags =
               parseNegation() ? builder.getNegatedClassAccessFlags() :
-                builder.getClassAccessFlags();
+                  builder.getClassAccessFlags();
           skipWhitespace();
           if (acceptString("public")) {
             flags.setPublic();
@@ -992,26 +992,20 @@
       }
     }
 
-    private void checkNotNegatedPattern() throws ProguardRuleParserException {
+    private ProguardClassNameList parseClassNames() throws ProguardRuleParserException {
+      ProguardClassNameList.Builder builder = ProguardClassNameList.builder();
       skipWhitespace();
-      if (acceptChar('!')) {
-        throw parseError("Negated filters are not supported");
-      }
-    }
-
-    private List<ProguardTypeMatcher> parseClassNames() throws ProguardRuleParserException {
-      List<ProguardTypeMatcher> classNames = new ArrayList<>();
-      checkNotNegatedPattern();
-      classNames
-          .add(ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
+      boolean negated = acceptChar('!');
+      builder.addClassName(negated,
+          ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
       skipWhitespace();
       while (acceptChar(',')) {
-        checkNotNegatedPattern();
-        classNames
-            .add(ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
+        negated = acceptChar('!');
+        builder.addClassName(negated,
+            ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
         skipWhitespace();
       }
-      return classNames;
+      return builder.build();
     }
 
     private String parsePackageNameOrEmptyString() {
@@ -1034,7 +1028,7 @@
       for (int lineNumber = 0; lineNumber < lines.length; lineNumber++) {
         String line = lines[lineNumber];
         if (remaining <= line.length() || lineNumber == lines.length - 1) {
-          String arrow = CharBuffer.allocate(remaining).toString().replace( '\0', ' ' ) + '^';
+          String arrow = CharBuffer.allocate(remaining).toString().replace('\0', ' ') + '^';
           return name + ":" + (lineNumber + 1) + ":" + remaining + "\n" + line
               + '\n' + arrow;
         }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index deb11a1..1cd7100 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -15,7 +15,7 @@
       DexAccessFlags negatedClassAccessFlags,
       boolean classTypeNegated,
       ProguardClassType classType,
-      List<ProguardTypeMatcher> classNames,
+      ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
index 787a31c..e449048 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
@@ -27,7 +27,7 @@
       DexAccessFlags negatedClassAccessFlags,
       boolean classTypeNegated,
       ProguardClassType classType,
-      List<ProguardTypeMatcher> classNames,
+      ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index fac7d99..1932c83 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -41,7 +41,7 @@
       DexAccessFlags negatedClassAccessFlags,
       boolean classTypeNegated,
       ProguardClassType classType,
-      List<ProguardTypeMatcher> classNames,
+      ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index fd123d4..9b06a2f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -65,6 +65,10 @@
   @Override
   public abstract int hashCode();
 
+  public DexType getSpecificType() {
+    return null;
+  }
+
   private static class MatchAllTypes extends ProguardTypeMatcher {
 
     private static final ProguardTypeMatcher MATCH_ALL_TYPES = new MatchAllTypes();
@@ -210,6 +214,11 @@
     public int hashCode() {
       return type.hashCode();
     }
+
+    @Override
+    public DexType getSpecificType() {
+      return type;
+    }
   }
 
   private static class MatchTypePattern extends ProguardTypeMatcher {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
index 581a0db..a679fde 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -27,7 +27,7 @@
       DexAccessFlags negatedClassAccessFlags,
       boolean classTypeNegated,
       ProguardClassType classType,
-      List<ProguardTypeMatcher> classNames,
+      ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 4dd1880..74cec83 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -108,22 +108,6 @@
     return anyImplementedInterfaceMatches(superClass, className, annotation);
   }
 
-  // Returns a list of types iff the keep rule only contains specific type matches.
-  // Otherwise, null is returned.
-  private DexType[] specificDexTypes(ProguardConfigurationRule rule) {
-    for (ProguardTypeMatcher matcher : rule.getClassNames()) {
-      if (!(matcher instanceof MatchSpecificType)) {
-        return null;
-      }
-    }
-    final int length = rule.getClassNames().size();
-    DexType[] result = new DexType[length];
-    for (int i = 0; i < length; i++) {
-      result[i] = ((MatchSpecificType) rule.getClassNames().get(i)).type;
-    }
-    return result;
-  }
-
   // Process a class with the keep rule.
   private void process(DexClass clazz, ProguardConfigurationRule rule) {
     if (!clazz.accessFlags.containsAllOf(rule.getClassAccessFlags())) {
@@ -168,51 +152,49 @@
       }
     }
 
-    for (ProguardTypeMatcher className : rule.getClassNames()) {
-      if (className.matches(clazz.type)) {
-        Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
-        if (rule instanceof ProguardKeepRule) {
-          switch (((ProguardKeepRule) rule).getType()) {
-            case KEEP_CLASS_MEMBERS: {
-              markMatchingVisibleMethods(clazz, memberKeepRules, rule, clazz.type);
-              markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
-              break;
-            }
-            case KEEP_CLASSES_WITH_MEMBERS: {
-              if (!allRulesSatisfied(memberKeepRules, clazz)) {
-                break;
-              }
-              // fallthrough;
-            }
-            case KEEP: {
-              markClass(clazz, rule);
-              markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
-              markMatchingFields(clazz, memberKeepRules, rule, null);
-              break;
-            }
-          }
-        } else if (rule instanceof ProguardCheckDiscardRule) {
-          if (memberKeepRules.isEmpty()) {
-            markClass(clazz, rule);
-          } else {
+    if (rule.getClassNames().matches(clazz.type)) {
+      Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
+      if (rule instanceof ProguardKeepRule) {
+        switch (((ProguardKeepRule) rule).getType()) {
+          case KEEP_CLASS_MEMBERS: {
+            markMatchingVisibleMethods(clazz, memberKeepRules, rule, clazz.type);
             markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
-            markMatchingMethods(clazz, memberKeepRules, rule, clazz.type);
+            break;
           }
-        } else if (rule instanceof ProguardWhyAreYouKeepingRule
-            || rule instanceof ProguardKeepPackageNamesRule) {
-          markClass(clazz, rule);
-          markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
-          markMatchingFields(clazz, memberKeepRules, rule, null);
-        } else if (rule instanceof ProguardAssumeNoSideEffectRule) {
-          markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
-          markMatchingFields(clazz, memberKeepRules, rule, null);
-        } else if (rule instanceof ProguardAlwaysInlineRule) {
-          markMatchingMethods(clazz, memberKeepRules, rule, null);
-        } else {
-          assert rule instanceof ProguardAssumeValuesRule;
-          markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
-          markMatchingFields(clazz, memberKeepRules, rule, null);
+          case KEEP_CLASSES_WITH_MEMBERS: {
+            if (!allRulesSatisfied(memberKeepRules, clazz)) {
+              break;
+            }
+            // fallthrough;
+          }
+          case KEEP: {
+            markClass(clazz, rule);
+            markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+            markMatchingFields(clazz, memberKeepRules, rule, null);
+            break;
+          }
         }
+      } else if (rule instanceof ProguardCheckDiscardRule) {
+        if (memberKeepRules.isEmpty()) {
+          markClass(clazz, rule);
+        } else {
+          markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
+          markMatchingMethods(clazz, memberKeepRules, rule, clazz.type);
+        }
+      } else if (rule instanceof ProguardWhyAreYouKeepingRule
+          || rule instanceof ProguardKeepPackageNamesRule) {
+        markClass(clazz, rule);
+        markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+        markMatchingFields(clazz, memberKeepRules, rule, null);
+      } else if (rule instanceof ProguardAssumeNoSideEffectRule) {
+        markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+        markMatchingFields(clazz, memberKeepRules, rule, null);
+      } else if (rule instanceof ProguardAlwaysInlineRule) {
+        markMatchingMethods(clazz, memberKeepRules, rule, null);
+      } else {
+        assert rule instanceof ProguardAssumeValuesRule;
+        markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+        markMatchingFields(clazz, memberKeepRules, rule, null);
       }
     }
   }
@@ -224,7 +206,7 @@
       // Mark all the things explicitly listed in keep rules.
       if (rules != null) {
         for (ProguardConfigurationRule rule : rules) {
-          DexType[] specifics = specificDexTypes(rule);
+          List<DexType> specifics = rule.getClassNames().asSpecificDexTypes();
           if (specifics != null) {
             // This keep rule only lists specific type matches.
             // This means there is no need to iterate over all classes.
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index 1573b34..92a755e 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -4,21 +4,29 @@
 package com.android.tools.r8.jasmin;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OutputMode;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import jasmin.ClassFile;
 import java.io.File;
@@ -28,6 +36,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
@@ -135,4 +144,65 @@
       throws IOException, CompilationException, ExecutionException {
     return ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), options);
   }
+
+  protected DexApplication buildApplication(JasminBuilder builder, InternalOptions options) {
+    try {
+      return buildApplication(AndroidApp.fromClassProgramData(builder.buildClasses()), options);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  protected DexApplication buildApplication(AndroidApp input, InternalOptions options) {
+    try {
+      options.itemFactory.resetSortedIndices();
+      return new ApplicationReader(input, options, new Timing("JasminTest")).read();
+    } catch (IOException | ExecutionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public String runArt(DexApplication application, InternalOptions options, String mainClass)
+      throws DexOverflowException {
+    try {
+      AndroidApp app = writeDex(application, options);
+      return runOnArt(app, mainClass);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public AndroidApp writeDex(DexApplication application, InternalOptions options)
+      throws DexOverflowException {
+    AppInfo appInfo = new AppInfo(application);
+    try {
+      return R8.writeApplication(
+          Executors.newSingleThreadExecutor(),
+          application,
+          appInfo,
+          null,
+          NamingLens.getIdentityLens(),
+          null,
+          null,
+          options);
+    } catch (ExecutionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  protected DexEncodedMethod getMethod(DexApplication application, String clazz,
+      MethodSignature signature) {
+    return getMethod(application,
+        clazz, signature.type, signature.name, signature.parameters);
+  }
+
+  protected DexEncodedMethod getMethod(DexApplication application, String className,
+      String returnType, String methodName, String[] parameters) {
+    DexInspector inspector = new DexInspector(application);
+    ClassSubject clazz = inspector.clazz(className);
+    assertTrue(clazz.isPresent());
+    MethodSubject method = clazz.method(returnType, methodName, Arrays.asList(parameters));
+    assertTrue(method.isPresent());
+    return method.getMethod();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/jasmin/Regress65432240.java b/src/test/java/com/android/tools/r8/jasmin/Regress65432240.java
new file mode 100644
index 0000000..e8efd38
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/Regress65432240.java
@@ -0,0 +1,72 @@
+// 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.IfNez;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class Regress65432240 extends JasminTestBase {
+
+  @Test
+  public void testConstantNotIntoEntryBlock() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    MethodSignature signature = clazz.addStaticMethod("test1", ImmutableList.of("I"), "I",
+        ".limit stack 3",
+        ".limit locals 2",
+        "  iload 0",
+        "  ifne L2",
+        "L1:",
+        "  iconst_0",
+        "  ireturn",
+        "L2:",
+        "  iload 0",
+        "  iload 0",
+        "  iconst_1",
+        "  isub",
+        "  invokestatic Test/test1(I)I",
+        "  iadd",
+        "  ireturn");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iconst_0",
+        "  invokestatic Test/test1(I)I",
+        "  invokestatic java/lang/Integer/toString(I)Ljava/lang/String;",
+        "  invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iconst_0",
+        "  invokestatic Test/test1(I)I",
+        "  invokestatic java/lang/Integer/toString(I)Ljava/lang/String;",
+        "  invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+        "  return");
+
+    String expected = runOnJava(builder, clazz.name);
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = process(originalApplication, options);
+
+    DexEncodedMethod method = getMethod(processedApplication, clazz.name, signature);
+    DexCode code = method.getCode().asDexCode();
+    assertTrue(code.instructions[0] instanceof IfNez);
+
+    String artResult = runArt(processedApplication, options, clazz.name);
+    assertEquals(expected, artResult);
+  }
+}
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 0f2e456..48f8835 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -129,8 +129,7 @@
     assertEquals(1, rules.size());
     ProguardConfigurationRule rule = rules.get(0);
     assertEquals(1, rule.getMemberRules().size());
-    assertEquals("com.company.hello.**", rule.getClassNames().get(0).toString());
-    assertEquals("com.company.world.**", rule.getClassNames().get(1).toString());
+    assertEquals("com.company.hello.**,com.company.world.**", rule.getClassNames().toString());
     assertEquals(ProguardKeepRuleType.KEEP, ((ProguardKeepRule) rule).getType());
     assertTrue(rule.getInheritanceIsExtends());
     assertEquals("some.library.Class", rule.getInheritanceClassName().toString());
@@ -188,7 +187,7 @@
     assertEquals(1, rules.size());
     ProguardConfigurationRule rule = rules.get(0);
     assertEquals(1, rule.getClassNames().size());
-    assertEquals("*", rule.getClassNames().get(0).toString());
+    assertEquals("*", rule.getClassNames().toString());
     assertTrue(rule.getInheritanceIsExtends());
     assertEquals("foo.bar", rule.getInheritanceClassName().toString());
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
index c38c379..af87086 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -18,87 +18,86 @@
 
   private static final DexItemFactory dexItemFactory = new DexItemFactory();
 
-  private static boolean matchTypeName(String pattern, String typeName,
-      DexItemFactory dexItemFactory) {
+  private static boolean matchTypeName(String typeName, String pattern) {
     return ProguardTypeMatcher.create(pattern, ClassOrType.TYPE, dexItemFactory)
         .matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName)));
   }
 
-  private static boolean matchClassName(String pattern, String className,
-      DexItemFactory dexItemFactory) {
-    return ProguardTypeMatcher.create(pattern, ClassOrType.CLASS, dexItemFactory)
+  private static boolean matchClassName(String className, String... patterns) {
+    ProguardClassNameList.Builder builder = ProguardClassNameList.builder();
+    for (String pattern : patterns) {
+      boolean isNegated = pattern.startsWith("!");
+      String actualPattern = isNegated ? pattern.substring(1) : pattern;
+      builder.addClassName(isNegated,
+          ProguardTypeMatcher.create(actualPattern, ClassOrType.CLASS, dexItemFactory));
+    }
+    return builder.build()
         .matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(className)));
   }
 
   @Test
   public void matchClassNames() {
-    assertTrue(matchClassName("**", "", dexItemFactory));
-    assertTrue(matchClassName("**", "a", dexItemFactory));
-    assertTrue(matchClassName("*", "", dexItemFactory));
-    assertTrue(matchClassName("*", "a", dexItemFactory));
-    assertFalse(matchClassName("?", "", dexItemFactory));
-    assertTrue(matchClassName("?", "a", dexItemFactory));
+    assertTrue(matchClassName("", "**"));
+    assertTrue(matchClassName("a", "**"));
+    assertTrue(matchClassName("", "*"));
+    assertTrue(matchClassName("a", "*"));
+    assertFalse(matchClassName("", "?"));
+    assertTrue(matchClassName("a", "?"));
 
-    assertTrue(matchClassName("**", "java.lang.Object", dexItemFactory));
-    assertFalse(
-        matchClassName("ja*", "java.lang.Object", dexItemFactory));
-    assertTrue(
-        matchClassName("ja**", "java.lang.Object", dexItemFactory));
-    assertTrue(matchClassName("ja**ject", "java.lang.Object",
-        dexItemFactory));
+    assertTrue(matchClassName("java.lang.Object", "**"));
+    assertFalse(matchClassName("java.lang.Object", "ja*"));
+    assertTrue(matchClassName("java.lang.Object", "ja**"));
+    assertTrue(matchClassName("java.lang.Object", "ja**ject"));
     // Oddly, the proguard specification for this makes a lonely * synonymous with **.
-    assertTrue(matchClassName("*", "java.lang.Object", dexItemFactory));
-    assertFalse(matchClassName("ja*ject", "java.lang.Object",
-        dexItemFactory));
+    assertTrue(matchClassName("java.lang.Object", "*"));
+    assertFalse(matchClassName("java.lang.Object", "ja*ject"));
 
-    assertTrue(matchClassName("java.*g.O*", "java.lang.Object",
-        dexItemFactory));
-    assertTrue(matchClassName("java.*g.O?je?t", "java.lang.Object",
-        dexItemFactory));
-    assertFalse(matchClassName("java.*g.O?je?t?", "java.lang.Object",
-        dexItemFactory));
-    assertFalse(matchClassName("java?lang.Object", "java.lang.Object",
-        dexItemFactory));
-    assertTrue(matchClassName("*a*.*a**", "java.lang.Object",
-        dexItemFactory));
-    assertTrue(matchClassName("*a**a**", "java.lang.Object",
-        dexItemFactory));
+    assertTrue(matchClassName("java.lang.Object", "java.*g.O*"));
+    assertTrue(matchClassName("java.lang.Object", "java.*g.O?je?t"));
+    assertFalse(matchClassName("java.lang.Object", "java.*g.O?je?t?"));
+    assertFalse(matchClassName("java.lang.Object", "java?lang.Object"));
+    assertTrue(matchClassName("java.lang.Object", "*a*.*a**"));
+    assertTrue(matchClassName("java.lang.Object", "*a**a**"));
+
+    assertTrue(matchClassName("java.lang.Object", "!java.util.**", "java**"));
+    assertFalse(matchClassName("java.lang.Object", "!java.**", "java.lang.*"));
+    assertTrue(matchClassName("java.lang.Object", "java.lang.*", "!java.**"));
   }
 
   private void assertMatchesBasicTypes(String pattern) {
     for (String type : BASIC_TYPES) {
-      assertTrue(matchTypeName(pattern, type, dexItemFactory));
+      assertTrue(matchTypeName(type, pattern));
     }
   }
 
   private void assertDoesNotMatchBasicTypes(String pattern) {
     for (String type : BASIC_TYPES) {
-      assertFalse(matchTypeName(pattern, type, dexItemFactory));
+      assertFalse(matchTypeName(type, pattern));
     }
   }
 
   @Test
   public void matchTypeNames() {
-    assertTrue(matchTypeName("**", "java.lang.Object", dexItemFactory));
+    assertTrue(matchTypeName("java.lang.Object", "**"));
     assertDoesNotMatchBasicTypes("**");
     assertDoesNotMatchBasicTypes("*");
     assertFalse(
-        matchTypeName("**", "java.lang.Object[]", dexItemFactory));
+        matchTypeName("java.lang.Object[]", "**"));
     assertFalse(
-        matchTypeName("**z", "java.lang.Object", dexItemFactory));
-    assertFalse(matchTypeName("java.**", "java.lang.Object[]",
-        dexItemFactory));
+        matchTypeName("java.lang.Object", "**z"));
+    assertFalse(matchTypeName("java.lang.Object[]", "java.**"
+    ));
     assertTrue(
-        matchTypeName("***", "java.lang.Object[]", dexItemFactory));
-    assertTrue(matchTypeName("***", "java.lang.Object[][]",
-        dexItemFactory));
-    assertFalse(matchTypeName("%", "java.lang.Object", dexItemFactory));
-    assertTrue(matchTypeName("**", "a", dexItemFactory));
-    assertTrue(matchTypeName("**[]", "java.lang.Object[]",
-        dexItemFactory));
-    assertFalse(matchTypeName("**[]", "java.lang.Object[][]",
-        dexItemFactory));
-    assertTrue(matchTypeName("*", "abc", dexItemFactory));
+        matchTypeName("java.lang.Object[]", "***"));
+    assertTrue(matchTypeName("java.lang.Object[][]", "***"
+    ));
+    assertFalse(matchTypeName("java.lang.Object", "%"));
+    assertTrue(matchTypeName("a", "**"));
+    assertTrue(matchTypeName("java.lang.Object[]", "**[]"
+    ));
+    assertFalse(matchTypeName("java.lang.Object[][]", "**[]"
+    ));
+    assertTrue(matchTypeName("abc", "*"));
     assertMatchesBasicTypes("***");
     assertMatchesBasicTypes("%");
   }
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 919f569..9c41f21 100644
--- a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
+++ b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
@@ -443,6 +443,7 @@
     DexCode code = method.getCode().asDexCode();
     // 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.
-    assertEquals(27, code.instructions.length);
+    // TODO(zerny): Consider optimizing the fallthrough branch of conditionals to not be return.
+    assertEquals(28, code.instructions.length);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
index 3750a86..21d3b9e 100644
--- a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
@@ -302,11 +302,10 @@
     DexCode code = method.getCode().asDexCode();
     if (key == 0) {
       assertEquals(5, code.instructions.length);
-      assertTrue(code.instructions[2] instanceof IfEqz);
+      assertTrue(code.instructions[0] instanceof IfEqz);
     } else {
       assertEquals(6, code.instructions.length);
-      assertTrue(some16BitConst(code.instructions[2]));
-      assertTrue(code.instructions[3] instanceof IfEq);
+      assertTrue(code.instructions[1] instanceof IfEq);
     }
   }
 
@@ -360,13 +359,13 @@
     DexEncodedMethod method = getMethod(app, signature);
     DexCode code = method.getCode().asDexCode();
     if (twoCaseWillUsePackedSwitch(key1, key2)) {
-      assertTrue(code.instructions[3] instanceof PackedSwitch);
+      assertTrue(code.instructions[0] instanceof PackedSwitch);
     } else {
       if (key1 == 0) {
-        assertTrue(code.instructions[3] instanceof IfEqz);
+        assertTrue(code.instructions[0] instanceof IfEqz);
       } else {
         // Const instruction before if.
-        assertTrue(code.instructions[4] instanceof IfEq);
+        assertTrue(code.instructions[1] instanceof IfEq);
       }
     }
   }