Merge "Do not alter already native/abstract methods while tree pruning."
diff --git a/.gitignore b/.gitignore
index 3bb46e9..1e383d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,8 @@
 third_party/android_jar/lib.tar.gz
 third_party/android_jar/lib-v[0-9][0-9]
 third_party/android_jar/lib-v[0-9][0-9].tar.gz
+third_party/benchmarks/kotlin-benches
+third_party/benchmarks/kotlin-benches.tar.gz
 third_party/benchmarks/santa-tracker
 third_party/benchmarks/santa-tracker.tar.gz
 third_party/desugar/desugar_*/
@@ -74,7 +76,8 @@
 gradlew
 gradlew.bat
 gradle/*
-r8.iml
+*.dex
+*.iml
 r8.ipr
 r8.iws
 #*#
diff --git a/AUTHORS b/AUTHORS
index 4d02335..d4aa190 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,3 +4,4 @@
 #   Name/Organization <email address>
 
 Google Inc.
+Uber Technologies Inc.
diff --git a/LIBRARY-LICENSE b/LIBRARY-LICENSE
index 889b9ad..58b6798 100644
--- a/LIBRARY-LICENSE
+++ b/LIBRARY-LICENSE
@@ -21,31 +21,31 @@
 - artifact: org.ow2.asm:asm-commons:+
   name: ASM Commons
   copyrightHolder: INRIA, France Telecom
-  license: ASM license
+  license: BSD
   licenseUrl: http://asm.ow2.org/license.html
   url: http://asm.ow2.org/index.html
 - artifact: org.ow2.asm:asm-tree:+
   name: ASM Tree
   copyrightHolder: INRIA, France Telecom
-  license: ASM license
+  license: BSD
   licenseUrl: http://asm.ow2.org/license.html
   url: http://asm.ow2.org/index.html
 - artifact: org.ow2.asm:asm-util:+
   name: ASM Util
   copyrightHolder: INRIA, France Telecom
-  license: ASM license
+  license: BSD
   licenseUrl: http://asm.ow2.org/license.html
   url: http://asm.ow2.org/index.html
 - artifact: org.ow2.asm:asm-analysis:+
   name: ASM Util
   copyrightHolder: INRIA, France Telecom
-  license: ASM license
+  license: BSD
   licenseUrl: http://asm.ow2.org/license.html
   url: http://asm.ow2.org/index.html
 - artifact: org.ow2.asm:asm:+
   name: ASM Core
   copyrightHolder: INRIA, France Telecom
-  license: ASM license
+  license: BSD
   licenseUrl: http://asm.ow2.org/license.html
   url: http://asm.ow2.org/index.html
 - artifact: org.jetbrains.kotlin:kotlin-stdlib:+
diff --git a/build.gradle b/build.gradle
index 52200e6..6825b7f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,7 @@
 ext {
     androidSupportVersion = '25.4.0'
     apacheCommonsVersion = '1.12'
-    asmVersion = '6.0'
+    asmVersion = '6.2.1'
     autoValueVersion = '1.5'
     espressoVersion = '3.0.0'
     fastutilVersion = '7.2.0'
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index e998206..400e5ba 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -63,8 +63,6 @@
 
   // Hidden ASM "synthetic attribute" bit we need to clear.
   private static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
-  // Descriptor used by ASM for missing annotations.
-  public static final String SYNTHETIC_ANNOTATION = "Ljava/lang/Synthetic;";
 
   private final JarApplicationReader application;
   private final Consumer<DexClass> classConsumer;
@@ -461,7 +459,7 @@
     private final int parameterCount;
     private List<DexAnnotation> annotations = null;
     private DexValue defaultAnnotation = null;
-    private int fakeParameterAnnotations = 0;
+    private int annotableParameterCount = -1;
     private List<List<DexAnnotation>> parameterAnnotationsLists = null;
     private List<DexValue> parameterNames = null;
     private List<DexValue> parameterFlags = null;
@@ -512,33 +510,30 @@
     }
 
     @Override
-    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
-      // ASM decided to workaround a javac bug that incorrectly deals with synthesized parameter
-      // annotations. However, that leads us to have different behavior than javac+jvm and
-      // dx+art. The workaround is to use a non-existing descriptor "Ljava/lang/Synthetic;" for
-      // exactly this case. In order to remove the workaround we ignore all annotations
-      // with that descriptor. If javac is fixed, the ASM workaround will not be hit and we will
-      // never see this non-existing annotation descriptor. ASM uses the same check to make
-      // sure to undo their workaround for the javac bug in their MethodWriter.
-      if (desc.equals(SYNTHETIC_ANNOTATION)) {
-        // We can iterate through all the parameters twice. Once for visible and once for
-        // invisible parameter annotations. We only record the number of fake parameter
-        // annotations once.
-        if (parameterAnnotationsLists == null) {
-          fakeParameterAnnotations++;
-        }
-        return null;
+    public void visitAnnotableParameterCount(int parameterCount, boolean visible) {
+      if (annotableParameterCount != -1) {
+        // TODO(113565942): We assume that the runtime visible and runtime invisible parameter
+        // count is always the same. It doesn't have to be according to the classfile format, so
+        // we really should split it into two sets.
+        assert annotableParameterCount == parameterCount;
       }
+      annotableParameterCount = parameterCount;
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
       if (parameterAnnotationsLists == null) {
-        int adjustedParameterCount = parameterCount - fakeParameterAnnotations;
-        parameterAnnotationsLists = new ArrayList<>(adjustedParameterCount);
-        for (int i = 0; i < adjustedParameterCount; i++) {
+        if (annotableParameterCount == -1) {
+          annotableParameterCount = parameterCount;
+        }
+        parameterAnnotationsLists = new ArrayList<>(annotableParameterCount);
+        for (int i = 0; i < annotableParameterCount; i++) {
           parameterAnnotationsLists.add(new ArrayList<>());
         }
       }
       assert mv == null;
       return createAnnotationVisitor(desc, visible,
-          parameterAnnotationsLists.get(parameter - fakeParameterAnnotations), parent.application);
+          parameterAnnotationsLists.get(parameter), parent.application);
     }
 
     @Override
@@ -596,7 +591,7 @@
         for (int i = 0; i < parameterAnnotationsLists.size(); i++) {
           sets[i] = createAnnotationSet(parameterAnnotationsLists.get(i));
         }
-        annotationsList = new ParameterAnnotationsList(sets, fakeParameterAnnotations);
+        annotationsList = new ParameterAnnotationsList(sets);
       }
       InternalOptions internalOptions = parent.application.options;
       if (parameterNames != null && internalOptions.canUseParameterNameAnnotations()) {
diff --git a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
index 23d5a38..343b392 100644
--- a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
+++ b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
@@ -60,6 +60,10 @@
     this.missingParameterAnnotations = missingParameterAnnotations;
   }
 
+  public int getAnnotableParameterCount() {
+    return size();
+  }
+
   @Override
   public int hashCode() {
     return Arrays.hashCode(values);
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 81e2fff..29b5e56 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
@@ -70,10 +70,36 @@
     if (block.getPredecessors().size() == 0) {
       throwUndefinedValueError();
     }
+
+    List<Value> operands = new ArrayList<>(block.getPredecessors().size());
     for (BasicBlock pred : block.getPredecessors()) {
       EdgeType edgeType = pred.getEdgeType(block);
-      // Since this read has been delayed we must provide the local info for the value.
       Value operand = builder.readRegister(register, type, pred, edgeType, readType);
+      operands.add(operand);
+    }
+
+    if (readType == RegisterReadType.DEBUG) {
+      for (Value operand : operands) {
+        if (type.meet(operand.outType()) == null) {
+          BasicBlock block = getBlock();
+          InstructionListIterator it = block.listIterator();
+          Value value = new Value(builder.getValueNumberGenerator().next(), type, null);
+          Position position = block.getPosition();
+          Instruction definition = new DebugLocalUninitialized(value);
+          definition.setBlock(block);
+          definition.setPosition(position);
+          it.add(definition);
+          // Update current definition and all users.
+          block.updateCurrentDefinition(register, value, EdgeType.NON_EDGE);
+          replaceUsers(value);
+          // Remove the phi from the block.
+          block.removePhi(this);
+          return;
+        }
+      }
+    }
+
+    for (Value operand : operands) {
       operand.constrainType(type);
       appendOperand(operand);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index ec39754..a835c08 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DebugLocalInfo;
@@ -29,7 +30,11 @@
 public class Value {
 
   public void constrainType(ValueType constraint) {
-    type = type.meet(constraint);
+    ValueType meet = type.meet(constraint);
+    if (meet == null) {
+      throw new CompilationError("Cannot compute meet of types: " + type + " and " + constraint);
+    }
+    type = meet;
   }
 
   public void markNonDebugLocalRead() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/ValueType.java b/src/main/java/com/android/tools/r8/ir/code/ValueType.java
index 69618b3..99f949d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ValueType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ValueType.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.code;
 
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexType;
@@ -50,7 +49,7 @@
     }
     if (isPreciseType() && other.isPreciseType()) {
       // Precise types must be identical, hitting the first check above.
-      throw new CompilationError("Cannot compute meet of types: " + this + " and " + other);
+      return null;
     }
     switch (this) {
       case OBJECT:
@@ -100,11 +99,10 @@
       default:
         throw new Unreachable("Unexpected value-type in meet: " + this);
     }
-    throw new CompilationError("Cannot compute meet of types: " + this + " and " + other);
+    return null;
   }
 
   public boolean verifyCompatible(ValueType other) {
-    // Computing meet will throw on incompatible types.
     assert meet(other) != null;
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index ae2e4dc..6fbafcb 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -49,6 +49,7 @@
 public class CfSourceCode implements SourceCode {
 
   private BlockInfo currentBlockInfo;
+  private boolean hasExitingInstruction = false;
 
   private static class TryHandlerList {
 
@@ -264,12 +265,14 @@
         return instructionIndex;
       }
       // If the throwable instruction is "throw" it closes the block.
+      hasExitingInstruction |= instruction instanceof CfThrow;
       return (instruction instanceof CfThrow) ? instructionIndex : -1;
     }
     if (isControlFlow(instruction)) {
       for (int target : getTargets(instructionIndex)) {
         builder.ensureNormalSuccessorBlock(instructionIndex, target);
       }
+      hasExitingInstruction |= instruction.isReturn();
       return instructionIndex;
     }
     return -1;
@@ -328,7 +331,6 @@
     setLocalVariableLists();
     buildArgumentInstructions(builder);
     recordStateForTarget(0, state.getSnapshot());
-    // TODO: addDebugLocalUninitialized + addDebugLocalStart for non-argument locals live at 0
     // TODO(b/109789541): Generate method synchronization for DEX backend.
     inPrelude = false;
   }
@@ -385,6 +387,20 @@
         builder.addDebugLocalStart(entry.getIntKey(), entry.getValue());
       }
     }
+
+    // If there are no explicit exits from the method (ie, this method is a loop without an explict
+    // return or an unhandled throw) then we cannot guarentee that a local live in a successor will
+    // ensure it is marked as such (via an explict 'end' marker) and thus be live in predecessors.
+    // In this case we insert an 'end' point on all explicit goto instructions ensuring that any
+    // back-edge will explicitly keep locals live at that point.
+    if (!hasExitingInstruction && code.getInstructions().get(predecessorOffset) instanceof CfGoto) {
+      assert !isExceptional;
+      for (Entry<DebugLocalInfo> entry : atSource.int2ObjectEntrySet()) {
+        if (atTarget.get(entry.getIntKey()) == entry.getValue()) {
+          builder.addDebugLocalEnd(entry.getIntKey(), entry.getValue());
+        }
+      }
+    }
   }
 
   @Override
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 22084a0..bb29da9 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
@@ -463,10 +463,12 @@
       it.previous();
       for (List<Value> values : uninitializedDebugLocalValues.values()) {
         for (Value value : values) {
-          Instruction def = new DebugLocalUninitialized(value);
-          def.setBlock(entryBlock);
-          def.setPosition(Position.none());
-          it.add(def);
+          if (value.isUsed()) {
+            Instruction def = new DebugLocalUninitialized(value);
+            def.setBlock(entryBlock);
+            def.setPosition(Position.none());
+            it.add(def);
+          }
         }
       }
     }
@@ -1891,7 +1893,7 @@
     }
 
     // Move all normal successors to the new block.
-    currentBlockInfo.normalSuccessors.forEach(info::addNormalSuccessor);
+    currentBlockInfo.normalSuccessors.forEach((Integer offset) -> info.addNormalSuccessor(offset));
     currentBlockInfo.normalSuccessors.clear();
 
     // Link the two blocks.
@@ -2237,6 +2239,10 @@
     return type != NumericType.FLOAT && type != NumericType.DOUBLE && type != NumericType.LONG;
   }
 
+  public ValueNumberGenerator getValueNumberGenerator() {
+    return valueNumberGenerator;
+  }
+
   @Override
   public String toString() {
     StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 7099296..34e43e3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -190,6 +190,8 @@
   private final DexMethod originalMethod;
   private final Position callerPosition;
 
+  private boolean hasExitingInstruction = false;
+
   public JarSourceCode(
       DexType clazz,
       MethodNode node,
@@ -400,6 +402,7 @@
   }
 
   private void computeBlockEntryJarStates(IRBuilder builder) {
+    hasExitingInstruction = false;
     Int2ReferenceSortedMap<BlockInfo> CFG = builder.getCFG();
     Queue<JarStateWorklistItem> worklist = new LinkedList<>();
     BlockInfo entry = CFG.get(IRBuilder.INITIAL_BLOCK_OFFSET);
@@ -419,21 +422,26 @@
         blockEnd += 1;
       }
       for (int i = item.instructionIndex; i < blockEnd; ++i) {
-        state.beginTransaction(i + 1, i + 1 != blockEnd);
+        boolean hasNextInstruction = i + 1 != blockEnd;
+        state.beginTransaction(i + 1, hasNextInstruction);
         AbstractInsnNode insn = getInstruction(i);
         updateState(insn);
         state.endTransaction();
+        if (!hasNextInstruction) {
+          hasExitingInstruction |=
+              isReturn(insn) || (isThrow(insn) && item.blockInfo.exceptionalSuccessors.isEmpty());
+        }
       }
       // At the end of the current block, propagate the state to all successors and add the ones
       // that changed to the worklist.
-      item.blockInfo.normalSuccessors.iterator().forEachRemaining(offset -> {
+      item.blockInfo.normalSuccessors.iterator().forEachRemaining((Integer offset) -> {
         if (state.recordStateForTarget(offset)) {
           if (offset >= 0) {
             worklist.add(new JarStateWorklistItem(CFG.get(offset.intValue()), offset));
           }
         }
       });
-      item.blockInfo.exceptionalSuccessors.iterator().forEachRemaining(offset -> {
+      item.blockInfo.exceptionalSuccessors.iterator().forEachRemaining((Integer offset) -> {
         if (state.recordStateForExceptionalTarget(offset)) {
           if (offset >= 0) {
             worklist.add(new JarStateWorklistItem(CFG.get(offset.intValue()), offset));
@@ -488,6 +496,18 @@
     for (Local toOpen : localChange.getLocalsToOpen()) {
       builder.addDebugLocalStart(toOpen.slot.register, toOpen.info);
     }
+
+    // If there are no explicit exits from the method (ie, this method is a loop without an explict
+    // return or an unhandled throw) then we cannot guarentee that a local live in a successor will
+    // ensure it is marked as such (via an explict 'end' marker) and thus be live in predecessors.
+    // In this case we insert an 'end' point on all explicit goto instructions ensuring that any
+    // back-edge will explicitly keep locals live at that point.
+    if (!hasExitingInstruction && getInstruction(predecessorOffset).getOpcode() == Opcodes.GOTO) {
+      assert !isExceptional;
+      for (Local local : localChange.getLocalsToPreserve()) {
+        builder.addDebugLocalEnd(local.slot.register, local.info);
+      }
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
index 22faeb1..cd08984 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -271,6 +271,17 @@
       this.state = state;
     }
 
+    public List<Local> getLocalsToPreserve() {
+      List<Local> live = new ArrayList<>(atExit.live.size());
+      for (LocalNodeInfo liveAtExit : atExit.live) {
+        if (atEntry.isLive(liveAtExit)) {
+          int register = state.getLocalRegister(liveAtExit.node.index, liveAtExit.type);
+          live.add(new Local(new Slot(register, liveAtExit.type), liveAtExit.info));
+        }
+      }
+      return live;
+    }
+
     public List<Local> getLocalsToClose() {
       List<Local> toClose = new ArrayList<>(atExit.live.size());
       for (LocalNodeInfo liveAtExit : atExit.live) {
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 b9ba46f..b91d9c7 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
@@ -1422,18 +1422,24 @@
 
   // Check if the static put is a constant derived from the class holding the method.
   // This checks for java.lang.Class.getName and java.lang.Class.getSimpleName.
-  private boolean isClassNameConstant(DexEncodedMethod method, StaticPut put) {
+  private boolean isClassNameConstantOf(DexClass clazz, StaticPut put) {
     if (put.getField().type != dexItemFactory.stringType) {
       return false;
     }
-    if (put.inValue().definition != null && put.inValue().definition.isInvokeVirtual()) {
-      InvokeVirtual invoke = put.inValue().definition.asInvokeVirtual();
+    if (put.inValue().definition != null) {
+      return isClassNameConstantOf(clazz, put.inValue().definition);
+    }
+    return false;
+  }
+
+  private boolean isClassNameConstantOf(DexClass clazz, Instruction instruction) {
+    if (instruction.isInvokeVirtual()) {
+      InvokeVirtual invoke = instruction.asInvokeVirtual();
       if ((invoke.getInvokedMethod() == dexItemFactory.classMethods.getSimpleName
           || invoke.getInvokedMethod() == dexItemFactory.classMethods.getName)
           && !invoke.inValues().get(0).isPhi()
           && invoke.inValues().get(0).definition.isConstClass()
-          && invoke.inValues().get(0).definition.asConstClass().getValue()
-          == method.method.getHolder()) {
+          && invoke.inValues().get(0).definition.asConstClass().getValue() == clazz.type) {
         return true;
       }
     }
@@ -1445,62 +1451,21 @@
       return;
     }
 
-    // Collect relevant static put which are dominated by the exit block, and not dominated by a
-    // static get.
-    // This does not check for instructions that can throw. However, as classes which throw in the
-    // class initializer are never visible to the program (see Java Virtual Machine Specification
-    // section 5.5, https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.5), this
-    // does not matter (except maybe for removal of const-string instructions, but that is
-    // acceptable).
-    if (code.computeNormalExitBlocks().isEmpty()) {
+    DexClass clazz = definitionFor(method.method.getHolder());
+    if (clazz == null) {
       return;
     }
-    DexClass clazz = definitionFor(method.method.getHolder());
-    assert clazz != null;
-    DominatorTree dominatorTree = new DominatorTree(code);
+
+    // Collect straight-line static puts up to the first side-effect that is not
+    // a static put on a field on this class with a value that can be hoisted to
+    // the field initial value.
     Set<StaticPut> puts = Sets.newIdentityHashSet();
-    Map<DexField, StaticPut> dominatingPuts = Maps.newIdentityHashMap();
-    for (BasicBlock block : dominatorTree.normalExitDominatorBlocks()) {
-      InstructionListIterator iterator = block.listIterator(block.getInstructions().size());
-      while (iterator.hasPrevious()) {
-        Instruction current = iterator.previous();
-        if (current.isStaticPut()) {
-          StaticPut put = current.asStaticPut();
-          DexField field = put.getField();
-          if (clazz.definesStaticField(field)) {
-            if (put.inValue().isConstant()) {
-              if ((field.type.isClassType() || field.type.isArrayType())
-                  && put.inValue().isZero()) {
-                // Collect put of zero as a potential default value.
-                dominatingPuts.putIfAbsent(put.getField(), put);
-                puts.add(put);
-              } else if (field.type.isPrimitiveType() || field.type == dexItemFactory.stringType) {
-                // Collect put as a potential default value.
-                dominatingPuts.putIfAbsent(put.getField(), put);
-                puts.add(put);
-              }
-            } else if (isClassNameConstant(method, put)) {
-              // Collect put of class name constant as a potential default value.
-              dominatingPuts.putIfAbsent(put.getField(), put);
-              puts.add(put);
-            }
-          }
-        }
-        if (current.isStaticGet()) {
-          // If a static field is read, any collected potential default value cannot be a
-          // default value.
-          DexField field = current.asStaticGet().getField();
-          if (dominatingPuts.containsKey(field)) {
-            dominatingPuts.remove(field);
-            Iterables.removeIf(puts, put -> put.getField() == field);
-          }
-        }
-      }
-    }
+    Map<DexField, StaticPut> finalFieldPut = Maps.newIdentityHashMap();
+    computeUnnecessaryStaticPuts(code, method, clazz, puts, finalFieldPut);
 
     if (!puts.isEmpty()) {
       // Set initial values for static fields from the definitive static put instructions collected.
-      for (StaticPut put : dominatingPuts.values()) {
+      for (StaticPut put : finalFieldPut.values()) {
         DexField field = put.getField();
         DexEncodedField encodedField = appInfo.definitionFor(field);
         if (field.type == dexItemFactory.stringType) {
@@ -1554,24 +1519,22 @@
         }
       }
 
-      // Remove the static put instructions now replaced by static filed initial values.
+      // Remove the static put instructions now replaced by static field initial values.
       List<Instruction> toRemove = new ArrayList<>();
-      for (BasicBlock block : dominatorTree.normalExitDominatorBlocks()) {
-        InstructionListIterator iterator = block.listIterator();
-        while (iterator.hasNext()) {
-          Instruction current = iterator.next();
-          if (current.isStaticPut() && puts.contains(current.asStaticPut())) {
-            iterator.remove();
-            // Collect, for removal, the instruction that created the value for the static put,
-            // if all users are gone. This is done even if these instructions can throw as for
-            // the current patterns matched these exceptions are not detectable.
-            StaticPut put = current.asStaticPut();
-            if (put.inValue().uniqueUsers().size() == 0) {
-              if (put.inValue().isConstString()) {
-                toRemove.add(put.inValue().definition);
-              } else if (put.inValue().definition.isInvokeVirtual()) {
-                toRemove.add(put.inValue().definition);
-              }
+      InstructionIterator iterator = code.instructionIterator();
+      while (iterator.hasNext()) {
+        Instruction current = iterator.next();
+        if (current.isStaticPut() && puts.contains(current.asStaticPut())) {
+          iterator.remove();
+          // Collect, for removal, the instruction that created the value for the static put,
+          // if all users are gone. This is done even if these instructions can throw as for
+          // the current patterns matched these exceptions are not detectable.
+          StaticPut put = current.asStaticPut();
+          if (put.inValue().uniqueUsers().size() == 0) {
+            if (put.inValue().isConstString()) {
+              toRemove.add(put.inValue().definition);
+            } else if (put.inValue().definition.isInvokeVirtual()) {
+              toRemove.add(put.inValue().definition);
             }
           }
         }
@@ -1579,18 +1542,70 @@
 
       // Remove the instructions collected for removal.
       if (toRemove.size() > 0) {
-        for (BasicBlock block : dominatorTree.normalExitDominatorBlocks()) {
-          InstructionListIterator iterator = block.listIterator();
-          while (iterator.hasNext()) {
-            if (toRemove.contains(iterator.next())) {
-              iterator.remove();
-            }
+        iterator = code.instructionIterator();
+        while (iterator.hasNext()) {
+          if (toRemove.contains(iterator.next())) {
+            iterator.remove();
           }
         }
       }
     }
   }
 
+  private void computeUnnecessaryStaticPuts(IRCode code, DexEncodedMethod clinit, DexClass clazz,
+      Set<StaticPut> puts, Map<DexField, StaticPut> finalFieldPut) {
+    final int color = code.reserveMarkingColor();
+    try {
+      BasicBlock block = code.blocks.getFirst();
+      while (!block.isMarked(color) && block.getPredecessors().size() <= 1) {
+        block.mark(color);
+        InstructionListIterator it = block.listIterator();
+        while (it.hasNext()) {
+          Instruction instruction = it.next();
+          if (instructionHasSideEffects(instruction)) {
+            if (isClassNameConstantOf(clazz, instruction)) {
+              continue;
+            } else if (instruction.isStaticPut()) {
+              StaticPut put = instruction.asStaticPut();
+              if (put.getField().clazz != clazz.type) {
+                // Can cause clinit on another class which can read uninitialized static fields
+                // of this class.
+                return;
+              }
+              DexField field = put.getField();
+              if (clazz.definesStaticField(field)) {
+                if (put.inValue().isConstant()) {
+                  if ((field.type.isClassType() || field.type.isArrayType())
+                      && put.inValue().isZero()) {
+                    finalFieldPut.put(put.getField(), put);
+                    puts.add(put);
+                  } else if (field.type.isPrimitiveType()
+                      || field.type == dexItemFactory.stringType) {
+                    finalFieldPut.put(put.getField(), put);
+                    puts.add(put);
+                  }
+                } else if (isClassNameConstantOf(clazz, put)) {
+                  // Collect put of class name constant as a potential default value.
+                  finalFieldPut.put(put.getField(), put);
+                  puts.add(put);
+                }
+              }
+            } else if (!(instruction.isConstString() || instruction.isConstClass())) {
+              // Allow const string and const class which can only throw exceptions as their
+              // side-effect. Bail out for anything else.
+              return;
+            }
+          }
+        }
+        if (block.exit().isGoto()) {
+          block = block.exit().asGoto().getTarget();
+        }
+      }
+    } finally {
+      code.returnMarkingColor(color);
+    }
+  }
+
   public DexClass definitionFor(DexType type) {
     return converter.definitionFor(type);
   }
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 1498be4..65bb387 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -34,7 +34,6 @@
 import com.android.tools.r8.graph.DexValue.UnknownDexValue;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.JarClassFileReader;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.ProguardMapSupplier;
@@ -283,19 +282,19 @@
 
   private void writeParameterAnnotations(
       MethodVisitor visitor, ParameterAnnotationsList parameterAnnotations) {
+    // TODO(113565942): We currently assume that the annotable parameter count
+    // it the same for visible and invisible annotations. That doesn't actually
+    // seem to be the case in the class file format.
+    visitor.visitAnnotableParameterCount(
+        parameterAnnotations.getAnnotableParameterCount(), true);
+    visitor.visitAnnotableParameterCount(
+        parameterAnnotations.getAnnotableParameterCount(), false);
+
     for (int i = 0; i < parameterAnnotations.size(); i++) {
-      if (parameterAnnotations.isMissing(i)) {
-        AnnotationVisitor av =
-            visitor.visitParameterAnnotation(i, JarClassFileReader.SYNTHETIC_ANNOTATION, false);
-        if (av != null) {
-          av.visitEnd();
-        }
-      } else {
-        int iFinal = i;
-        writeAnnotations(
-            (d, vis) -> visitor.visitParameterAnnotation(iFinal, d, vis),
-            parameterAnnotations.get(i).annotations);
-      }
+      int iFinal = i;
+      writeAnnotations(
+          (d, vis) -> visitor.visitParameterAnnotation(iFinal, d, vis),
+          parameterAnnotations.get(i).annotations);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index a846ea7..a8ec999 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -368,7 +368,7 @@
     assert name.endsWith(CLASS_EXTENSION) :
         "Name " + name + " must have " + CLASS_EXTENSION + " suffix";
     String descriptor = name.substring(0, name.length() - CLASS_EXTENSION.length());
-    if (descriptor.contains(".")) {
+    if (descriptor.indexOf(JAVA_PACKAGE_SEPARATOR) != -1) {
       throw new CompilationError("Unexpected class file name: " + name);
     }
     return 'L' + descriptor + ';';
@@ -383,6 +383,6 @@
 
   public static String getPathFromJavaType(String typeName) {
     assert isValidJavaType(typeName);
-    return typeName.replace(".", "/") + ".class";
+    return typeName.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR) + ".class";
   }
 }
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index cb4a4af..35767fb 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -144,14 +144,6 @@
     byte[] dump() throws Exception;
   }
 
-  protected static Class loadClassFromAsmClass(AsmDump asmDump) {
-    try {
-      return loadClassFromDump(asmDump.dump());
-    } catch (Exception e) {
-      throw new ClassFormatError(e.toString());
-    }
-  }
-
   protected static byte[] getBytesFromAsmClass(AsmDump asmDump) {
     try {
       return asmDump.dump();
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
index 70e8445..6f40632 100644
--- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
@@ -205,8 +205,8 @@
         "regress_37658666.Regress",
         (expectedBytes, actualBytes) -> {
           // javac emits LDC(-0.0f) instead of the shorter FCONST_0 FNEG emitted by CfConstNumber.
-          String ldc = "mv.visitLdcInsn(new Float(\"-0.0\"));";
-          String constNeg = "mv.visitInsn(FCONST_0);\nmv.visitInsn(FNEG);";
+          String ldc = "methodVisitor.visitLdcInsn(new Float(\"-0.0\"));";
+          String constNeg = "methodVisitor.visitInsn(FCONST_0);\nmethodVisitor.visitInsn(FNEG);";
           assertEquals(asmToString(expectedBytes).replace(ldc, constNeg), asmToString(actualBytes));
         });
   }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 182326e..bf900a9 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.objectweb.asm.Opcodes.ASM6;
 
 import com.android.tools.r8.DataResourceProvider.Visitor;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
@@ -52,6 +53,8 @@
 import java.util.zip.ZipOutputStream;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
 
 public class TestBase {
   public enum Backend {
@@ -602,7 +605,7 @@
 
   protected ProcessResult runOnJavaRawNoVerify(String main, List<byte[]> classes, List<String> args)
       throws IOException {
-    return ToolHelper.runJavaNoVerify(Collections.singletonList(writeToZip(classes)), main, args);
+    return ToolHelper.runJavaNoVerify(Collections.singletonList(writeToJar(classes)), main, args);
   }
 
   protected ProcessResult runOnJavaRaw(String main, byte[]... classes) throws IOException {
@@ -615,7 +618,7 @@
     mainAndArgs.add(main);
     mainAndArgs.addAll(args);
     return ToolHelper.runJava(
-        Collections.singletonList(writeToZip(classes)), mainAndArgs.toArray(new String[0]));
+        Collections.singletonList(writeToJar(classes)), mainAndArgs.toArray(new String[0]));
   }
 
   protected ProcessResult runOnJavaRaw(AndroidApp app, String mainClass, List<String> args)
@@ -628,33 +631,57 @@
     return ToolHelper.runJava(out, mainAndArgs.toArray(new String[0]));
   }
 
-  protected Path writeToZip(List<byte[]> classes) throws IOException {
-    File result = temp.newFile("tmp.zip");
-    try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(result.toPath(),
-        StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
+  private String extractClassName(byte[] ccc) {
+    class ClassNameExtractor extends ClassVisitor {
+      private String className;
+
+      private ClassNameExtractor() {
+        super(ASM6);
+      }
+
+      @Override
+      public void visit(
+          int version,
+          int access,
+          String name,
+          String signature,
+          String superName,
+          String[] interfaces) {
+        className =
+            name.replace(
+                DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR,
+                DescriptorUtils.JAVA_PACKAGE_SEPARATOR);
+      }
+
+      String getClassName() {
+        return className;
+      }
+    }
+
+    ClassReader reader = new ClassReader(ccc);
+    ClassNameExtractor extractor = new ClassNameExtractor();
+    reader.accept(
+        extractor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+    return extractor.getClassName();
+  }
+
+  protected Path writeToJar(List<byte[]> classes) throws IOException {
+    Path result = File.createTempFile("junit", ".jar", temp.getRoot()).toPath();
+    try (ZipOutputStream out =
+        new ZipOutputStream(
+            Files.newOutputStream(
+                result, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
       for (byte[] clazz : classes) {
-        String name = loadClassFromDump(clazz).getTypeName();
+        String name = extractClassName(clazz);
         ZipUtils.writeToZipStream(
             out, DescriptorUtils.getPathFromJavaType(name), clazz, ZipEntry.STORED);
       }
     }
-    return result.toPath();
+    return result;
   }
 
-  protected Path writeToZip(JasminBuilder jasminBuilder) throws Exception {
-    return writeToZip(jasminBuilder.buildClasses());
-  }
-
-  protected static Class loadClassFromDump(byte[] dump) {
-    return new DumpLoader().loadClass(dump);
-  }
-
-  private static class DumpLoader extends ClassLoader {
-
-    @SuppressWarnings("deprecation")
-    public Class loadClass(byte[] clazz) {
-      return defineClass(clazz, 0, clazz.length);
-    }
+  protected Path writeToJar(JasminBuilder jasminBuilder) throws Exception {
+    return writeToJar(jasminBuilder.buildClasses());
   }
 
   /**
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
index 80541e8..6387b81 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
@@ -9,52 +9,62 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
-import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.VmTestRunner;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import org.junit.runner.RunWith;
 
-@RunWith(VmTestRunner.class)
 abstract class AccessRelaxationTestBase extends TestBase {
 
-  static R8Command.Builder loadProgramFiles(Iterable<Class> classes) {
+  final Backend backend;
+
+  AccessRelaxationTestBase(Backend backend) {
+    this.backend = backend;
+  }
+
+  static R8Command.Builder loadProgramFiles(Backend backend, Iterable<Class> classes) {
     R8Command.Builder builder = R8Command.builder();
     for (Class clazz : classes) {
       builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
     }
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
-    builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
+    if (backend == Backend.DEX) {
+      builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+    }
     return builder;
   }
 
-  static R8Command.Builder loadProgramFiles(Package p, Class... classes) throws Exception {
+  static R8Command.Builder loadProgramFiles(Backend backend, Package p, Class... classes)
+      throws Exception {
     R8Command.Builder builder = R8Command.builder();
     builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(p));
     for (Class clazz : classes) {
       builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
     }
-    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
-    builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+    builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
+    if (backend == Backend.DEX) {
+      builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+    }
     return builder;
   }
 
-  void compareJvmAndArt(AndroidApp app, Class mainClass) throws Exception {
+  void compareReferenceJVMAndProcessed(AndroidApp app, Class mainClass) throws Exception {
     // Run on Jvm.
     String jvmOutput = runOnJava(mainClass);
-
-    // Run on Art to check generated code against verifier.
-    String artOutput = runOnArt(app, mainClass);
-
-    String adjustedArtOutput = artOutput.replace(
-        "java.lang.IncompatibleClassChangeError", "java.lang.IllegalAccessError");
-    assertEquals(jvmOutput, adjustedArtOutput);
+    String output;
+    if (backend == Backend.DEX) {
+      output = runOnArt(app, mainClass);
+    } else {
+      assert backend == Backend.CF;
+      output = runOnJava(app, mainClass);
+    }
+    String adjustedOutput =
+        output.replace("java.lang.IncompatibleClassChangeError", "java.lang.IllegalAccessError");
+    assertEquals(jvmOutput, adjustedOutput);
   }
 
   static void assertPublic(CodeInspector codeInspector, Class clazz, MethodSignature signature) {
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
index 48b511c..2b66d93 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
@@ -15,8 +15,12 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
 class L1 {
   private final String x;
@@ -154,16 +158,26 @@
   }
 }
 
+@RunWith(Parameterized.class)
 public final class ConstructorRelaxationTest extends AccessRelaxationTestBase {
   private static final String INIT= "<init>";
   private static final List<Class> CLASSES =
       ImmutableList.of(L1.class, L2_1.class, L2_2.class, L3_1.class, L3_2.class);
 
+  @Parameterized.Parameters(name = "Backend: {0}")
+  public static Collection<Backend> data() {
+    return Arrays.asList(Backend.values());
+  }
+
+  public ConstructorRelaxationTest(Backend backend) {
+    super(backend);
+  }
+
   @Test
   public void test() throws Exception {
     Class mainClass = CtorTestMain.class;
     R8Command.Builder builder =
-        loadProgramFiles(Iterables.concat(CLASSES, ImmutableList.of(mainClass)));
+        loadProgramFiles(backend, Iterables.concat(CLASSES, ImmutableList.of(mainClass)));
     builder.addProguardConfiguration(
         ImmutableList.of(
             "-keep class " + mainClass.getCanonicalName() + "{",
@@ -186,7 +200,7 @@
               options.enableInlining = false;
               options.enableVerticalClassMerging = false;
             });
-    compareJvmAndArt(app, mainClass);
+    compareReferenceJVMAndProcessed(app, mainClass);
 
     CodeInspector codeInspector = new CodeInspector(app);
     for (Class clazz : CLASSES) {
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
index 5b641dc..c63f425 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
@@ -20,15 +20,30 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.Collection;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
+@RunWith(Parameterized.class)
 public final class NonConstructorRelaxationTest extends AccessRelaxationTestBase {
+
+  @Parameterized.Parameters(name = "Backend: {0}")
+  public static Collection<Backend> data() {
+    return Arrays.asList(Backend.values());
+  }
+
+  public NonConstructorRelaxationTest(Backend backend) {
+    super(backend);
+  }
+
   private static final String STRING = "java.lang.String";
 
   @Test
   public void testStaticMethodRelaxation() throws Exception {
     Class mainClass = C.class;
-    R8Command.Builder builder = loadProgramFiles(mainClass.getPackage());
+    R8Command.Builder builder = loadProgramFiles(backend, mainClass.getPackage());
 
     // Note: we use '-checkdiscard' to indirectly check that the access relaxation is
     // done which leads to inlining of all pB*** methods so they are removed. Without
@@ -60,7 +75,7 @@
         Origin.unknown());
 
     AndroidApp app = ToolHelper.runR8(builder.build());
-    compareJvmAndArt(app, mainClass);
+    compareReferenceJVMAndProcessed(app, mainClass);
 
     CodeInspector codeInspector = new CodeInspector(app);
     assertPublic(codeInspector, A.class,
@@ -82,7 +97,7 @@
   @Test
   public void testInstanceMethodRelaxation() throws Exception {
     Class mainClass = TestMain.class;
-    R8Command.Builder builder = loadProgramFiles(mainClass.getPackage());
+    R8Command.Builder builder = loadProgramFiles(backend, mainClass.getPackage());
 
     builder.addProguardConfiguration(
         ImmutableList.of(
@@ -108,7 +123,7 @@
         Origin.unknown());
 
     AndroidApp app = ToolHelper.runR8(builder.build());
-    compareJvmAndArt(app, mainClass);
+    compareReferenceJVMAndProcessed(app, mainClass);
 
     CodeInspector codeInspector = new CodeInspector(app);
     assertPublic(codeInspector, Base.class,
diff --git a/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java b/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
index 3a83e30..83dbd9e 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
@@ -83,10 +83,9 @@
         }
         currentLine = nextLine;
         while (frameEntryLines.size() > nextDepth + 1) {
-          // If the depth decreases by more than one we have popped the filtered frames.
-          // Verify they are placeholder lines.
-          int placeholder = frameEntryLines.pop();
-          assert placeholder == PLACEHOLDER_LINE;
+          // If the depth decreases by more than one we have popped the filtered frames or an
+          // exception has unwinded the stack.
+          frameEntryLines.pop();
         }
         int lineOnEntry = frameEntryLines.pop();
         assert nextDepth == frameEntryLines.size();
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 50f67d4..8af8922 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -1171,6 +1171,22 @@
           Assert.fail("expected local '" + localName + "' is not present at " + locationString);
         }
 
+        private void checkIncorrectLocal(String localName, Value expected, Value actual) {
+          if (expected.equals(actual)) {
+            return;
+          }
+          String locationString = convertCurrentLocationToString();
+          Assert.fail(
+              "Incorrect value for local '"
+                  + localName
+                  + "' at "
+                  + locationString
+                  + ", expected "
+                  + expected
+                  + ", actual "
+                  + actual);
+        }
+
         @Override
         public void checkNoLocal(String localName) {
           Optional<Variable> localVar = getVariableAt(mirror, getLocation(), localName);
@@ -1207,8 +1223,7 @@
           assert valuesCount == 1;
           Value localValue = replyPacket.getNextValueAsValue();
 
-          Assert.assertEquals("Incorrect value for local '" + localName + "'",
-              expectedValue, localValue);
+          checkIncorrectLocal(localName, expectedValue, localValue);
         }
 
         /**
diff --git a/src/test/java/com/android/tools/r8/debug/NonExitingMethodTest.java b/src/test/java/com/android/tools/r8/debug/NonExitingMethodTest.java
new file mode 100644
index 0000000..967b7aa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/NonExitingMethodTest.java
@@ -0,0 +1,33 @@
+// 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.debug;
+
+public class NonExitingMethodTest {
+
+  private static int bValue = 0;
+
+  private static int b() {
+    if (bValue == 1) throw new RuntimeException();
+    return ++bValue;
+  }
+
+  public void foo(int arg) {
+    int x = 1;
+    int y = 2;
+    while (true) {
+      int z = b();
+      x = y;
+      y = z;
+    }
+  }
+
+  public static void main(String[] args) {
+    try {
+      new NonExitingMethodTest().foo(42);
+    } catch (RuntimeException e) {
+      return;
+    }
+    throw new RuntimeException("Expected exception...");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/NonExitingMethodTestRunner.java b/src/test/java/com/android/tools/r8/debug/NonExitingMethodTestRunner.java
new file mode 100644
index 0000000..3b78abd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/NonExitingMethodTestRunner.java
@@ -0,0 +1,60 @@
+// 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.debug;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import java.io.IOException;
+import java.nio.file.Path;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class NonExitingMethodTestRunner extends DebugTestBase {
+
+  public static final Class CLASS = NonExitingMethodTest.class;
+  public static final String FILE = CLASS.getSimpleName() + ".java";
+
+  private static Path getClassFilePath() {
+    return ToolHelper.getClassFileForTestClass(CLASS);
+  }
+
+  public DebugTestConfig cfConfig() {
+    return new CfDebugTestConfig(ToolHelper.getClassPathForTests());
+  }
+
+  public DebugTestConfig d8Config() {
+    return new D8DebugTestConfig().compileAndAdd(temp, getClassFilePath());
+  }
+
+  public DebugTestConfig r8CfConfig() throws CompilationFailedException, IOException {
+    Path path = temp.getRoot().toPath().resolve("out.jar");
+    ToolHelper.runR8(
+        R8Command.builder()
+            .setMode(CompilationMode.DEBUG)
+            .addProgramFiles(getClassFilePath())
+            .setProgramConsumer(new ClassFileConsumer.ArchiveConsumer(path))
+            .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+            .build(),
+        options -> options.enableCfFrontend = true);
+    return new CfDebugTestConfig().addPaths(path);
+  }
+
+  @Test
+  public void test() throws Exception {
+    Assume.assumeTrue(
+        "Skipping test "
+            + testName.getMethodName()
+            + " because debug tests are not yet supported on Windows",
+        !ToolHelper.isWindows());
+    new DebugStreamComparator()
+        .add("CF", streamDebugTest(cfConfig(), CLASS.getCanonicalName(), NO_FILTER))
+        .add("D8", streamDebugTest(d8Config(), CLASS.getCanonicalName(), NO_FILTER))
+        .add("R8/CF", streamDebugTest(r8CfConfig(), CLASS.getCanonicalName(), NO_FILTER))
+        .setFilter(s -> s.getSourceFile().equals(FILE))
+        .compare();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 4405565..060d87b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -324,7 +324,7 @@
         "return"
     );
 
-    Path output = writeToZip(builder);
+    Path output = writeToJar(builder);
     addExtraClasspath(output);
     return JASMIN_MAIN_CLASS;
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index a3aa5e1..036d960 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -476,7 +476,7 @@
         "return"
     );
 
-    Path javaOutput = writeToZip(jasminBuilder);
+    Path javaOutput = writeToJar(jasminBuilder);
     ProcessResult javaResult = ToolHelper.runJava(javaOutput, "Foo");
     if (javaResult.exitCode != 0) {
       System.err.println(javaResult.stderr);
diff --git a/src/test/java/com/android/tools/r8/regress/b113326860/B113326860.java b/src/test/java/com/android/tools/r8/regress/b113326860/B113326860.java
new file mode 100644
index 0000000..294dbbc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b113326860/B113326860.java
@@ -0,0 +1,159 @@
+// 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.regress.b113326860;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.code.Sput;
+import com.android.tools.r8.code.SputBoolean;
+import com.android.tools.r8.code.SputObject;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+class TestClassDoOptimize {
+  private static boolean b = false;
+  private static int i = 3;
+  private static Class clazz;
+  static {
+    clazz = TestClassDoOptimize.class;
+    b = true;
+    i = 42;
+  }
+}
+
+class TestClassDoNotOptimize {
+
+  private static boolean initialized = false;
+  private static final TestClassDoNotOptimize INSTANCE;
+
+  TestClassDoNotOptimize() {
+    if (!initialized) {
+      System.out.println("Not initialized as expected");
+    }
+  }
+
+  static {
+    INSTANCE = new TestClassDoNotOptimize();
+    initialized = true;
+  }
+}
+
+class TestClassDoNotOptimize2 {
+  static boolean b = false;
+  static {
+    boolean forcingOtherClassInit = TestClassDoNotOptimize3.b;
+    b = true;
+  }
+
+  static void m() {
+    System.out.println(b);
+  }
+}
+
+class TestClassDoNotOptimize3 {
+  static boolean b = false;
+  static {
+    TestClassDoNotOptimize2.m();
+  }
+}
+
+class TestDoWhileLoop {
+  static int i = 0;
+  static int j = 0;
+  static {
+    do {
+      i = 42;
+      System.out.println(i);
+      j = j + 1;
+      i = 10;
+    } while (j < 10);
+  }
+
+  public static void main(String[] args) {
+    System.out.println(TestDoWhileLoop.i);
+  }
+}
+
+public class B113326860 {
+
+  private CodeInspector compileTestClasses(List<Class> classes)
+      throws IOException, CompilationFailedException, ExecutionException {
+    D8Command.Builder builder = D8Command.builder().setMode(CompilationMode.RELEASE);
+    for (Class c : classes) {
+      builder.addClassProgramData(ToolHelper.getClassAsBytes(c), Origin.unknown());
+    }
+    AndroidApp app = ToolHelper.runD8(builder);
+    return new CodeInspector(app);
+  }
+
+  @Test
+  public void optimizedClassInitializer()
+      throws IOException, CompilationFailedException, ExecutionException {
+    CodeInspector inspector = compileTestClasses(ImmutableList.of(TestClassDoOptimize.class));
+    ClassSubject clazz = inspector.clazz(TestClassDoOptimize.class);
+    assertThat(clazz, isPresent());
+    MethodSubject method = clazz.method("void", "<clinit>", ImmutableList.of());
+    assertThat(method, isPresent());
+    assertFalse(Arrays.stream(method.getMethod().getCode().asDexCode().instructions)
+        .anyMatch(i -> i instanceof SputBoolean || i instanceof Sput));
+    assertTrue(Arrays.stream(method.getMethod().getCode().asDexCode().instructions)
+        .anyMatch(i -> i instanceof SputObject));
+  }
+
+  @Test
+  public void nonOptimizedClassInitializer()
+      throws ExecutionException, CompilationFailedException, IOException {
+    CodeInspector inspector =
+        compileTestClasses(ImmutableList.of(TestClassDoNotOptimize.class));
+    ClassSubject clazz = inspector.clazz(TestClassDoNotOptimize.class);
+    assertThat(clazz, isPresent());
+    MethodSubject method = clazz.method("void", "<clinit>", ImmutableList.of());
+    assertThat(method, isPresent());
+    assertTrue(Arrays.stream(method.getMethod().getCode().asDexCode().instructions)
+        .anyMatch(i -> i instanceof SputBoolean));
+  }
+
+  @Test
+  public void nonOptimizedClassInitializer2()
+      throws ExecutionException, CompilationFailedException, IOException {
+    CodeInspector inspector = compileTestClasses(
+        ImmutableList.of(TestClassDoNotOptimize2.class, TestClassDoNotOptimize3.class));
+    ClassSubject clazz = inspector.clazz(TestClassDoNotOptimize2.class);
+    assertThat(clazz, isPresent());
+    MethodSubject method = clazz.method("void", "<clinit>", ImmutableList.of());
+    assertThat(method, isPresent());
+    assertTrue(Arrays.stream(method.getMethod().getCode().asDexCode().instructions)
+        .anyMatch(i -> i instanceof SputBoolean));
+  }
+
+  @Test
+  public void doWhileLoop() throws ExecutionException, CompilationFailedException, IOException {
+    CodeInspector inspector = compileTestClasses(ImmutableList.of(TestDoWhileLoop.class));
+    ClassSubject clazz = inspector.clazz(TestDoWhileLoop.class);
+    assertThat(clazz, isPresent());
+    MethodSubject method = clazz.method("void", "<clinit>", ImmutableList.of());
+    assertThat(method, isPresent());
+    // Leave the const 42 and the assignment in there!
+    assertTrue(Arrays.stream(method.getMethod().getCode().asDexCode().instructions)
+        .anyMatch(i -> i instanceof SingleConstant && (((SingleConstant) i).decodedValue() == 42)));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.j b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.j
new file mode 100644
index 0000000..acae3df
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.j
@@ -0,0 +1,487 @@
+;; 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.
+.class Test
+.super java/lang/Object
+.method test()Z
+.limit stack 10
+.limit locals 21
+.var 8 is foobar F from L27 to L68
+.catch all from L1 to L5 using L10
+.catch all from L6 to L9 using L10
+.catch all from L10 to L11 using L10
+L0:
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  astore 1
+  aload 1
+  monitorenter
+L1:
+  nop
+L2:
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  invokevirtual com.example.Baz.valid()Z
+  ifne L6
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  invokevirtual com.example.Baz.parse()Z
+  ifne L6
+L3:
+  ldc 0
+L4:
+  istore 4
+L5:
+  aload 1
+  monitorexit
+  iload 4
+  ireturn
+L6:
+  nop
+L7:
+  getstatic kotlin/Unit/INSTANCE Lkotlin/Unit;
+L8:
+  astore 3
+L9:
+  aload 1
+  monitorexit
+  goto L12
+L10:
+  astore 3
+L11:
+  aload 1
+  monitorexit
+  aload 3
+  athrow
+L12:
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  invokevirtual com.example.Baz.getLong()J
+  lstore 1
+L13:
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  invokevirtual com.example.Baz.getLength()I
+  istore 3
+L14:
+  aload 0
+  iload 3
+  ldc 4
+  imul
+  newarray float
+  putfield com/example/Foo/rects [F
+L15:
+  iload 3
+  ifne L17
+L16:
+  ldc 1
+  ireturn
+L17:
+  ldc 127
+  istore 4
+L18:
+  ldc 0
+  istore 5
+L19:
+  ldc 0
+  istore 6
+  iload 3
+  istore 7
+L20:
+  iload 6
+  iload 7
+  if_icmpge L25
+L21:
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  iload 6
+  invokevirtual com.example.Baz.getInt(I)I
+  istore 8
+  iload 4
+  iload 8
+  invokestatic java.lang.Math.min(II)I
+  istore 4
+L22:
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  iload 6
+  invokevirtual com.example.Baz.getInt(I)I
+  istore 8
+  iload 5
+  iload 8
+  invokestatic java.lang.Math.max(II)I
+  istore 5
+L23:
+  iinc 6 1
+L24:
+  goto L20
+L25:
+  aload 0
+  getfield com/example/Foo/height F
+  ldc 2
+  i2f
+  aload 0
+  getfield com/example/Foo/margin F
+  fmul
+  fsub
+  fstore 6
+L26:
+  iload 5
+  iload 4
+  isub
+  ldc 1
+  iadd
+  istore 7
+L27:
+  aload 0
+  getfield com/example/Foo/rMinGap F
+  aload 0
+  getfield com/example/Foo/rMaxGap F
+  fcmpg
+  ifge L28
+  ldc 1
+  goto L29
+L28:
+  ldc 0
+L29:
+  istore 11
+  iload 11
+  ifne L30
+  ldc "Failed requirement."
+  astore 12
+  new java/lang/IllegalArgumentException
+  dup
+  aload 12
+  invokevirtual java.lang.Object.toString()Ljava/lang/String;
+  invokespecial java.lang.IllegalArgumentException.<init>(Ljava/lang/String;)V
+  checkcast java/lang/Throwable
+  athrow
+L30:
+  aload 0
+  getfield com/example/Foo/rMinHeight F
+  aload 0
+  getfield com/example/Foo/rMaxHeight F
+  fcmpg
+  ifge L31
+  ldc 1
+  goto L32
+L31:
+  ldc 0
+L32:
+  istore 11
+  iload 11
+  ifne L33
+  ldc "Failed requirement."
+  astore 12
+  new java/lang/IllegalArgumentException
+  dup
+  aload 12
+  invokevirtual java.lang.Object.toString()Ljava/lang/String;
+  invokespecial java.lang.IllegalArgumentException.<init>(Ljava/lang/String;)V
+  checkcast java/lang/Throwable
+  athrow
+L33:
+  iload 7
+  ldc 1
+  if_icmpgt L37
+L34:
+  aload 0
+  getfield com/example/Foo/rMaxHeight F
+  fstore 9
+L35:
+  fload 9
+  aload 0
+  getfield com/example/Foo/rMaxGap F
+  fadd
+  fstore 8
+L36:
+  aload 0
+  getfield com/example/Foo/margin F
+  fload 6
+  fload 9
+  fsub
+  ldc 2
+  i2f
+  fdiv
+  fadd
+  fstore 10
+  goto L54
+L37:
+  aload 0
+  getfield com/example/Foo/rMinHeight F
+  iload 7
+  i2f
+  fmul
+  aload 0
+  getfield com/example/Foo/rMinGap F
+  iload 7
+  ldc 1
+  isub
+  i2f
+  fmul
+  fadd
+  fload 6
+  fcmpl
+  ifle L41
+L38:
+  aload 0
+  getfield com/example/Foo/rMinHeight F
+  fstore 9
+L39:
+  fload 6
+  fload 9
+  fsub
+  iload 7
+  ldc 1
+  isub
+  i2f
+  fdiv
+  fstore 8
+L40:
+  aload 0
+  getfield com/example/Foo/margin F
+  fstore 10
+  goto L54
+L41:
+  aload 0
+  getfield com/example/Foo/rMaxHeight F
+  iload 7
+  i2f
+  fmul
+  aload 0
+  getfield com/example/Foo/rMinGap F
+  iload 7
+  ldc 1
+  isub
+  i2f
+  fmul
+  fadd
+  fload 6
+  fcmpl
+  ifle L45
+L42:
+  fload 6
+  aload 0
+  getfield com/example/Foo/rMinGap F
+  iload 7
+  ldc 1
+  isub
+  i2f
+  fmul
+  fsub
+  iload 7
+  i2f
+  fdiv
+  fstore 9
+L43:
+  fload 9
+  aload 0
+  getfield com/example/Foo/rMinGap F
+  fadd
+  fstore 8
+L44:
+  aload 0
+  getfield com/example/Foo/margin F
+  fstore 10
+  goto L54
+L45:
+  aload 0
+  getfield com/example/Foo/rMaxHeight F
+  iload 7
+  i2f
+  fmul
+  aload 0
+  getfield com/example/Foo/rMaxGap F
+  iload 7
+  ldc 1
+  isub
+  i2f
+  fmul
+  fadd
+  fload 6
+  fcmpl
+  ifle L51
+L46:
+  aload 0
+  getfield com/example/Foo/rMaxHeight F
+  fstore 9
+L47:
+  fload 6
+  aload 0
+  getfield com/example/Foo/rMaxHeight F
+  iload 7
+  i2f
+  fmul
+  fsub
+  iload 7
+  ldc 1
+  isub
+  i2f
+  fdiv
+  fstore 11
+L48:
+  fload 9
+  fload 11
+  fadd
+  fstore 8
+L49:
+  aload 0
+  getfield com/example/Foo/margin F
+  fstore 10
+L50:
+  goto L54
+L51:
+  aload 0
+  getfield com/example/Foo/rMaxHeight F
+  fstore 9
+L52:
+  fload 9
+  aload 0
+  getfield com/example/Foo/rMaxGap F
+  fadd
+  fstore 8
+L53:
+  aload 0
+  getfield com/example/Foo/margin F
+  fload 6
+  fload 9
+  iload 7
+  i2f
+  fmul
+  aload 0
+  getfield com/example/Foo/rMaxGap F
+  iload 7
+  ldc 1
+  isub
+  i2f
+  fmul
+  fadd
+  fsub
+  ldc 2
+  i2f
+  fdiv
+  fadd
+  fstore 10
+L54:
+  aload 0
+  getfield com/example/Foo/converter Lcom/example/FooBar;
+  invokevirtual com.example.FooBar.getFloat()F
+  fstore 11
+L55:
+  ldc 0
+  istore 12
+  iload 3
+  istore 13
+L56:
+  iload 12
+  iload 13
+  if_icmpge L67
+L57:
+  iload 12
+  ldc 4
+  imul
+  istore 14
+L58:
+  aload 0
+  getfield com/example/Foo/rects [F
+  iload 14
+  ldc 0
+  iadd
+  fload 11
+  aload 0
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  iload 12
+  invokevirtual com.example.Baz.getLong(I)J
+  lload 1
+  aload 0
+  getfield com/example/Foo/bpm I
+  invokespecial com.example.FooBar.getDouble(JJI)D
+  d2f
+  fmul
+  fastore
+L59:
+  aload 0
+  getfield com/example/Foo/rects [F
+L60:
+  iload 14
+  ldc 2
+  iadd
+L61:
+  aload 0
+  getfield com/example/Foo/rects [F
+  iload 14
+  ldc 0
+  iadd
+  faload
+  ldc 1.0
+  fstore 15
+  fload 11
+  aload 0
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  iload 12
+  invokevirtual com.example.Baz.getLong(I)J
+  lload 1
+  aload 0
+  getfield com/example/Foo/bpm I
+  invokespecial com.example.FooBar.getDouble(JJI)D
+  d2f
+  fmul
+  fstore 16
+  fstore 19
+  istore 18
+  astore 17
+  fload 15
+  fload 16
+  invokestatic java.lang.Math.max(FF)F
+  fstore 20
+  aload 17
+  iload 18
+  fload 19
+  fload 20
+L62:
+  fadd
+  fastore
+L63:
+  aload 0
+  getfield com/example/Foo/rects [F
+  iload 14
+  ldc 1
+  iadd
+  fload 10
+  iload 5
+  aload 0
+  getfield com/example/Foo/bar Lcom/example/Baz;
+  iload 12
+  invokevirtual com.example.Baz.getInt(I)I
+  isub
+  i2f
+  fload 8
+  fmul
+  fadd
+  fastore
+L64:
+  aload 0
+  getfield com/example/Foo/rects [F
+  iload 14
+  ldc 3
+  iadd
+  aload 0
+  getfield com/example/Foo/rects [F
+  iload 14
+  ldc 1
+  iadd
+  faload
+  fload 9
+  fadd
+  fastore
+L65:
+  iinc 12 1
+L66:
+  goto L56
+L67:
+  ldc 1
+  ireturn
+L68:
+.end method
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
new file mode 100644
index 0000000..a62622e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
@@ -0,0 +1,42 @@
+// 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.regress.b113347830;
+
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
+import jasmin.ClassFile;
+import java.io.ByteArrayOutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import org.junit.Test;
+
+public class B113347830 {
+
+  public static final Class CLASS = B113347830.class;
+  public static final String NAME = CLASS.getSimpleName();
+
+  @Test
+  public void test() throws Exception {
+    ClassFile jasminFile = new ClassFile();
+    jasminFile.readJasmin(
+        Files.newBufferedReader(
+            Paths.get(
+                ToolHelper.TESTS_DIR, "java", CLASS.getCanonicalName().replace('.', '/') + ".j")),
+        "Test",
+        false);
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    jasminFile.write(out);
+    byte[] bytes = out.toByteArray();
+
+    D8.run(
+        D8Command.builder()
+            .addClassProgramData(bytes, Origin.unknown())
+            .setDisableDesugaring(true)
+            .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+            .build());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
index d3784ee..7937f4e 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
@@ -8,7 +8,9 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.code.IfEqz;
 import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.SgetBoolean;
 import com.android.tools.r8.code.Sput;
 import com.android.tools.r8.code.SputObject;
 import com.android.tools.r8.graph.DexCode;
@@ -387,28 +389,20 @@
     CodeInspector inspector = new CodeInspector(processedApplication);
     assertTrue(inspector.clazz("Test").clinit().isPresent());
 
-    DexValue value;
     assertTrue(inspector.clazz("Test").field("int", "intField").hasExplicitStaticValue());
-    value = inspector.clazz("Test").field("int", "intField").getStaticValue();
+    DexValue value = inspector.clazz("Test").field("int", "intField").getStaticValue();
     assertTrue(value instanceof DexValueInt);
-    assertEquals(3, ((DexValueInt) value).getValue());
+    assertEquals(1, ((DexValueInt) value).getValue());
 
     assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("java.lang.String", "stringField").getStaticValue();
     assertTrue(value instanceof DexValueString);
-    assertEquals(("7"), ((DexValueString) value).getValue().toString());
+    // We stop at control-flow and therefore the initial value for the string field will be 5.
+    assertEquals(("5"), ((DexValueString) value).getValue().toString());
 
     DexCode code = inspector.clazz("Test").clinit().getMethod().getCode().asDexCode();
-    for (Instruction instruction : code.instructions) {
-      if (instruction instanceof Sput) {
-        Sput put = (Sput) instruction;
-        // Only int put ot intField2.
-        assertEquals(put.getField().name.toString(), "intField2");
-      } else {
-        // No Object (String) puts.
-        assertFalse(instruction instanceof SputObject);
-      }
-    }
+    assertTrue(code.instructions[0] instanceof SgetBoolean);
+    assertTrue(code.instructions[1] instanceof IfEqz);
 
     String result = runArt(processedApplication);
 
diff --git a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
index a93db7a..acf4488 100644
--- a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
@@ -55,7 +55,7 @@
 
   @Before
   public void setUp() throws Exception {
-    libJar = writeToZip(ImmutableList.of(ToolHelper.getClassAsBytes(GoingToBeMissed.class)));
+    libJar = writeToJar(ImmutableList.of(ToolHelper.getClassAsBytes(GoingToBeMissed.class)));
     libDex = temp.getRoot().toPath().resolve("lib.zip");
     AndroidApp libApp = ToolHelper.runD8(readClasses(GoingToBeMissed.class));
     libApp.writeToZip(libDex, OutputMode.DexIndexed);