Merge "Disable assertion in vertical class merger"
diff --git a/.gitignore b/.gitignore
index 14e24ec..c04d9dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,6 +63,8 @@
 third_party/kotlin
 third_party/nest/*
 !third_party/nest/*.sha1
+third_party/opensource_apps
+third_party/opensource_apps.tar.gz
 third_party/photos/*
 !third_party/photos/*.sha1
 third_party/proguard/*
diff --git a/build.gradle b/build.gradle
index f129fb5..4ce2e18 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,6 @@
 ext {
     androidSupportVersion = '25.4.0'
     asmVersion = '6.2.1'
-    autoValueVersion = '1.5'
     espressoVersion = '3.0.0'
     fastutilVersion = '7.2.0'
     guavaVersion = '23.0'
@@ -66,7 +65,6 @@
     dependencies {
         classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
         classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
-        classpath "net.ltgt.gradle:gradle-apt-plugin:0.12"
         classpath "com.gradle:build-scan-plugin:1.14"
     }
 }
@@ -81,7 +79,6 @@
 apply plugin: 'java'
 apply plugin: 'idea'
 apply plugin: 'net.ltgt.errorprone-base'
-apply plugin: "net.ltgt.apt"
 
 if (project.hasProperty('with_code_coverage')) {
     apply plugin: 'jacoco'
@@ -257,7 +254,6 @@
     examplesCompile "com.google.guava:guava:$guavaVersion"
     examplesCompile "junit:junit:$junitVersion"
     examplesCompile "org.mockito:mockito-core:$mockitoVersion"
-    examplesCompileOnly "com.google.auto.value:auto-value:$autoValueVersion"
     supportLibs "com.android.support:support-v4:$androidSupportVersion"
     supportLibs "junit:junit:$junitVersion"
     supportLibs "com.android.support.test.espresso:espresso-core:$espressoVersion"
@@ -266,7 +262,6 @@
     debugTestResourcesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     examplesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
-    apt "com.google.auto.value:auto-value:$autoValueVersion"
 }
 
 def r8LibPath = "$buildDir/libs/r8lib.jar"
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 3eda1b7..a84b916 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -45,6 +45,10 @@
     return opcode;
   }
 
+  public boolean isInterface() {
+    return itf;
+  }
+
   @Override
   public void write(MethodVisitor visitor, NamingLens lens) {
     String owner = lens.lookupInternalName(method.getHolder());
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index d45fd83..f956b53 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -638,14 +638,15 @@
     return builder.build();
   }
 
-  public DexEncodedMethod toForwardingMethod(DexClass holder, DexItemFactory itemFactory) {
+  public DexEncodedMethod toForwardingMethod(DexClass holder, AppInfo appInfo) {
     checkIfObsolete();
     // Clear the final flag, as this method is now overwritten. Do this before creating the builder
     // for the forwarding method, as the forwarding method will copy the access flags from this,
     // and if different forwarding methods are created in different subclasses the first could be
     // final.
     accessFlags.demoteFromFinal();
-    DexMethod newMethod = itemFactory.createMethod(holder.type, method.proto, method.name);
+    DexMethod newMethod =
+        appInfo.dexItemFactory.createMethod(holder.type, method.proto, method.name);
     Invoke.Type type = accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.SUPER;
     Builder builder = builder(this);
     builder.setMethod(newMethod);
@@ -655,6 +656,7 @@
       builder.accessFlags.setAbstract();
     } else {
       // Create code that forwards the call to the target.
+      DexClass target = appInfo.definitionFor(method.holder);
       builder.setCode(
           new SynthesizedCode(
               callerPosition ->
@@ -665,7 +667,8 @@
                       accessFlags.isStatic() ? null : method.holder,
                       method,
                       type,
-                      callerPosition),
+                      callerPosition,
+                      target.isInterface()),
               registry -> {
                 if (accessFlags.isStatic()) {
                   registry.registerInvokeStatic(method);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 14d7ffb..40756ae 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.code;
 
-import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isVisibleFromOriginalContext;
-
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.cf.TypeVerificationHelper;
 import com.android.tools.r8.cf.code.CfFieldInstruction;
@@ -21,7 +19,6 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
@@ -101,24 +98,6 @@
   }
 
   @Override
-  public boolean canBeDeadCode(AppInfo appInfo, IRCode code) {
-    // instance-get can be dead code as long as it cannot have any of the following:
-    // * NoSuchFieldError (resolution failure)
-    // * IllegalAccessError (not visible from the access context)
-    // * NullPointerException (null receiver).
-    // TODO(b/123857022): Should be possible to use definitionFor().
-    DexEncodedField resolvedField = appInfo.resolveFieldOn(getField().getHolder(), getField());
-    if (resolvedField == null) {
-      return false;
-    }
-    if (code == null
-        || !isVisibleFromOriginalContext(appInfo, code.method.method.getHolder(), resolvedField)) {
-      return false;
-    }
-    return object().getTypeLattice().nullability().isDefinitelyNotNull();
-  }
-
-  @Override
   public int maxInValueRegister() {
     return Constants.U4BIT_MAX;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 80e74f3..b79d076 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
-import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isVisibleFromOriginalContext;
-
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.cf.TypeVerificationHelper;
 import com.android.tools.r8.cf.code.CfFieldInstruction;
@@ -20,7 +18,6 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
@@ -94,25 +91,6 @@
   }
 
   @Override
-  public boolean canBeDeadCode(AppInfo appInfo, IRCode code) {
-    // static-get can be dead as long as it cannot have any of the following:
-    // * NoSuchFieldError (resolution failure)
-    // * IllegalAccessError (not visible from the access context)
-    // * side-effects in <clinit>
-    // TODO(b/123857022): Should be possible to use definitionFor().
-    DexEncodedField resolvedField = appInfo.resolveFieldOn(getField().getHolder(), getField());
-    if (resolvedField == null) {
-      return false;
-    }
-    if (code == null
-        || !isVisibleFromOriginalContext(appInfo, code.method.method.getHolder(), resolvedField)) {
-      return false;
-    }
-    return appInfo.hasSubtyping()
-        && !getField().getHolder().classInitializationMayHaveSideEffects(appInfo);
-  }
-
-  @Override
   public int maxInValueRegister() {
     return Constants.U8BIT_MAX;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 8a46e1e..743f2a5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.cf.code.CfFrame;
 import com.android.tools.r8.cf.code.CfFrame.FrameType;
 import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.cf.code.CfTryCatch;
@@ -21,6 +22,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
 import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -179,9 +181,23 @@
     CodeRewriter.collapseTrivialGotos(method, code);
     DexBuilder.removeRedundantDebugPositions(code);
     CfCode code = buildCfCode();
+    assert verifyInvokeInterface(code, appInfo);
     return code;
   }
 
+  private static boolean verifyInvokeInterface(CfCode code, AppInfo appInfo) {
+    for (CfInstruction instruction : code.instructions) {
+      if (instruction instanceof CfInvoke) {
+        CfInvoke invoke = (CfInvoke) instruction;
+        if (invoke.getMethod().holder.isClassType()) {
+          DexClass holder = appInfo.definitionFor(invoke.getMethod().holder);
+          assert holder == null || holder.isInterface() == invoke.isInterface();
+        }
+      }
+    }
+    return true;
+  }
+
   public DexField resolveField(DexField field) {
     DexEncodedField resolvedField = appInfo.resolveFieldOn(field.clazz, field);
     return resolvedField == null ? field : resolvedField.field;
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 31b0ec6..3f355c1 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
@@ -1405,19 +1405,6 @@
     add(Invoke.create(type, item, callSiteProto, null, arguments, itf));
   }
 
-  public void addInvoke(Type type, DexItem item, DexProto callSiteProto, List<Value> arguments) {
-    addInvoke(type, item, callSiteProto, arguments, false);
-  }
-
-  public void addInvoke(
-      Type type,
-      DexItem item,
-      DexProto callSiteProto,
-      List<ValueType> types,
-      List<Integer> registers) {
-    addInvoke(type, item, callSiteProto, types, registers, false);
-  }
-
   public void addInvoke(
       Type type,
       DexItem item,
@@ -1518,7 +1505,9 @@
       registerIndex += constraint.requiredRegisters();
     }
     checkInvokeArgumentRegisters(registerIndex, argumentRegisterCount);
-    addInvoke(type, method, callSiteProto, arguments);
+    // Note: We only call this register variant from DEX inputs where isInterface does not matter.
+    assert !isGeneratingClassFiles();
+    addInvoke(type, method, callSiteProto, arguments, false /* isInterface */);
   }
 
   public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters) {
@@ -1538,7 +1527,7 @@
       registerIndex += constraint.requiredRegisters();
     }
     checkInvokeArgumentRegisters(registerIndex, argumentCount);
-    addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
+    addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments, false /* isInterface */);
   }
 
   public void addMultiNewArray(DexType type, int dest, int[] dimensions) {
@@ -1547,7 +1536,7 @@
     for (int dimension : dimensions) {
       arguments.add(readRegister(dimension, ValueTypeConstraint.INT));
     }
-    addInvoke(Invoke.Type.MULTI_NEW_ARRAY, type, null, arguments);
+    addInvoke(Invoke.Type.MULTI_NEW_ARRAY, type, null, arguments, false /* isInterface */);
     addMoveResult(dest);
   }
 
@@ -1581,7 +1570,9 @@
       register += valueTypeConstraint.requiredRegisters();
     }
     checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
-    addInvoke(type, method, callSiteProto, arguments);
+    // Note: We only call this register variant from DEX inputs where isInterface does not matter.
+    assert !isGeneratingClassFiles();
+    addInvoke(type, method, callSiteProto, arguments, false /* isInterface */);
   }
 
   public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister) {
@@ -1597,7 +1588,9 @@
       register += constraint.requiredRegisters();
     }
     checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
-    addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
+    // Note: We only call this register variant from DEX inputs where isInterface does not matter.
+    assert !isGeneratingClassFiles();
+    addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments, false /* isInterface */);
   }
 
   private void checkInvokeArgumentRegisters(int expected, int actual) {
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 1387f75..93e5355 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
@@ -2864,7 +2864,13 @@
     List<ValueType> argumentTypes = Arrays.asList(valueType(CLASS_TYPE), valueType(INT_ARRAY_TYPE));
     List<Integer> argumentRegisters = Arrays.asList(classDestTemp, dimensionsDestTemp);
     builder.ensureBlockForThrowingInstruction();
-    builder.addInvoke(Invoke.Type.STATIC, newInstance, null, argumentTypes, argumentRegisters);
+    builder.addInvoke(
+        Invoke.Type.STATIC,
+        newInstance,
+        null,
+        argumentTypes,
+        argumentRegisters,
+        false /* isInterface */);
     // Pop the temporaries and push the final result.
     state.pop(); // classDestTemp.
     state.pop(); // dimensionsDestTemp.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index 3554806..81f93a7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -109,8 +109,17 @@
     }
 
     // Method call to the original impl-method.
-    add(builder -> builder.addInvoke(inferInvokeType(),
-        implMethod, implMethod.proto, argValueTypes, argRegisters));
+    // Mirroring assert in constructor, we never need accessors to interfaces.
+    assert !descriptor().implHandle.type.isInvokeInterface();
+    add(
+        builder ->
+            builder.addInvoke(
+                inferInvokeType(),
+                implMethod,
+                implMethod.proto,
+                argValueTypes,
+                argRegisters,
+                false /* isInterface */));
 
     // Does the method have return value?
     if (proto.returnType == factory().voidType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index d2a233b..bff3ca1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -100,10 +100,10 @@
 
   private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) {
     DexMethod method = defaultMethod.method;
+    DexClass target = rewriter.findDefinitionFor(method.holder);
     // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
     // even if this results in invalid code, these classes are never desugared.
-    assert rewriter.findDefinitionFor(method.holder) != null
-        && !rewriter.findDefinitionFor(method.holder).isLibraryClass();
+    assert target != null && !target.isLibraryClass();
     // New method will have the same name, proto, and also all the flags of the
     // default method, including bridge flag.
     DexMethod newMethod = rewriter.factory.createMethod(clazz.type, method.proto, method.name);
@@ -124,7 +124,8 @@
                     null /* static method */,
                     rewriter.defaultAsMethodOfCompanionClass(method),
                     Invoke.Type.STATIC,
-                    callerPosition)));
+                    callerPosition,
+                    target.isInterface())));
   }
 
   // For a given class `clazz` inspects all interfaces it implements directly or
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 955abf0..466dc83 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -175,7 +175,8 @@
                         method.method,
                         Invoke.Type.VIRTUAL,
                         callerPosition,
-                        true)));
+                        false /* isInterface */,
+                        true /* castResult */)));
     // Optimize to generate DexCode instead of SynthesizedCode.
     converter.optimizeSynthesizedMethod(newVirtualMethod);
     return newVirtualMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 01b647b..fd875af 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -252,7 +252,8 @@
                           null,
                           origMethod,
                           Type.STATIC,
-                          callerPosition)));
+                          callerPosition,
+                          true /* isInterface */)));
       newEncodedMethod.getMutableOptimizationInfo().markNeverInline();
       dispatchMethods.add(newEncodedMethod);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
index 7e5cd81..9507471 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
@@ -46,8 +46,15 @@
     }
 
     // Method call to the main functional interface method.
-    add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL,
-        this.mainMethod, this.mainMethod.proto, argValueTypes, argRegisters));
+    add(
+        builder ->
+            builder.addInvoke(
+                Invoke.Type.VIRTUAL,
+                this.mainMethod,
+                this.mainMethod.proto,
+                argValueTypes,
+                argRegisters,
+                false /* isInterface */));
 
     // Does the method have return value?
     if (proto.returnType == factory().voidType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
index 2aa4630..cec70bb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
@@ -24,12 +24,15 @@
     // Create and initialize an instance.
     int instance = nextRegister(ValueType.OBJECT);
     add(builder -> builder.addNewInstance(instance, lambda.type));
-    add(builder -> builder.addInvoke(
-        Invoke.Type.DIRECT,
-        lambda.constructor,
-        lambda.constructor.proto,
-        ImmutableList.of(ValueType.OBJECT),
-        ImmutableList.of(instance)));
+    add(
+        builder ->
+            builder.addInvoke(
+                Invoke.Type.DIRECT,
+                lambda.constructor,
+                lambda.constructor.proto,
+                ImmutableList.of(ValueType.OBJECT),
+                ImmutableList.of(instance),
+                false /* isInterface */));
 
     // Assign to a field.
     add(builder -> builder.addStaticPut(instance, lambda.instanceField));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
index 7f2acc3..b2b9486 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
@@ -23,8 +23,14 @@
   protected void prepareInstructions() {
     // Super constructor call (always java.lang.Object.<init>()).
     DexMethod objectInitMethod = lambda.rewriter.objectInitMethod;
-    add(builder -> builder.addInvoke(Invoke.Type.DIRECT, objectInitMethod,
-        objectInitMethod.proto, Collections.singletonList(getReceiverValue())));
+    add(
+        builder ->
+            builder.addInvoke(
+                Invoke.Type.DIRECT,
+                objectInitMethod,
+                objectInitMethod.proto,
+                Collections.singletonList(getReceiverValue()),
+                false /* isInterface */));
 
     // Assign capture fields.
     DexType[] capturedTypes = captures();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index fb06f31..99112d7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -224,8 +224,15 @@
     }
 
     // Method call to the method implementing lambda or method-ref.
-    add(builder -> builder.addInvoke(target.invokeType,
-        methodToCall, methodToCall.proto, argValueTypes, argRegisters));
+    add(
+        builder ->
+            builder.addInvoke(
+                target.invokeType,
+                methodToCall,
+                methodToCall.proto,
+                argValueTypes,
+                argRegisters,
+                false /* isInterface */));
 
     // Does the method have return value?
     if (enforcedReturnType.isVoidType()) {
@@ -446,8 +453,15 @@
 
     List<ValueType> argValueTypes = ImmutableList.of(ValueType.OBJECT);
     List<Integer> argRegisters = Collections.singletonList(register);
-    add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL,
-        method, method.proto, argValueTypes, argRegisters));
+    add(
+        builder ->
+            builder.addInvoke(
+                Invoke.Type.VIRTUAL,
+                method,
+                method.proto,
+                argValueTypes,
+                argRegisters,
+                false /* isInterface */));
 
     ValueType valueType = ValueType.fromDexType(primitiveType);
     int result = nextRegister(valueType);
@@ -469,8 +483,15 @@
     ValueType valueType = ValueType.fromDexType(primitiveType);
     List<ValueType> argValueTypes = ImmutableList.of(valueType);
     List<Integer> argRegisters = Collections.singletonList(register);
-    add(builder -> builder.addInvoke(Invoke.Type.STATIC,
-        method, method.proto, argValueTypes, argRegisters));
+    add(
+        builder ->
+            builder.addInvoke(
+                Invoke.Type.STATIC,
+                method,
+                method.proto,
+                argValueTypes,
+                argRegisters,
+                false /* isInterface */));
 
     int result = nextRegister(ValueType.OBJECT);
     add(builder -> builder.addMoveResult(result));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
index 5177e4a..c6aecbb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
@@ -41,18 +41,27 @@
     List<ValueType> argTypes = Lists.newArrayList(ValueType.OBJECT, ValueType.INT);
     List<Integer> argRegisters = Lists.newArrayList(instance, lambdaId);
 
-    group.forEachLambda(info -> {
-      DexType lambda = info.clazz.type;
-      if (group.isSingletonLambda(lambda)) {
-        int id = group.lambdaId(lambda);
-        add(builder -> builder.addNewInstance(instance, groupClassType));
-        add(builder -> builder.addConst(TypeLatticeElement.INT, lambdaId, id));
-        add(builder -> builder.addInvoke(Type.DIRECT,
-            lambdaConstructorMethod, lambdaConstructorMethod.proto, argTypes, argRegisters));
-        add(builder -> builder.addStaticPut(
-            instance, group.getSingletonInstanceField(factory, id)));
-      }
-    });
+    group.forEachLambda(
+        info -> {
+          DexType lambda = info.clazz.type;
+          if (group.isSingletonLambda(lambda)) {
+            int id = group.lambdaId(lambda);
+            add(builder -> builder.addNewInstance(instance, groupClassType));
+            add(builder -> builder.addConst(TypeLatticeElement.INT, lambdaId, id));
+            add(
+                builder ->
+                    builder.addInvoke(
+                        Type.DIRECT,
+                        lambdaConstructorMethod,
+                        lambdaConstructorMethod.proto,
+                        argTypes,
+                        argRegisters,
+                        false /* isInterface*/));
+            add(
+                builder ->
+                    builder.addStaticPut(instance, group.getSingletonInstanceField(factory, id)));
+          }
+        });
 
     assert this.nextInstructionIndex() > 0 : "no single field initialized";
     add(IRBuilder::addReturn);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index e8fcd40..2bdc441 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -215,8 +215,15 @@
 
     @Override
     void prepareSuperConstructorCall(int receiverRegister) {
-      add(builder -> builder.addInvoke(Type.DIRECT, objectInitializer, objectInitializer.proto,
-          Lists.newArrayList(ValueType.OBJECT), Lists.newArrayList(receiverRegister)));
+      add(
+          builder ->
+              builder.addInvoke(
+                  Type.DIRECT,
+                  objectInitializer,
+                  objectInitializer.proto,
+                  Lists.newArrayList(ValueType.OBJECT),
+                  Lists.newArrayList(receiverRegister),
+                  false /* isInterface */));
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index f677b46..ee0b800 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -233,9 +233,15 @@
     void prepareSuperConstructorCall(int receiverRegister) {
       int arityRegister = nextRegister(ValueType.INT);
       add(builder -> builder.addConst(TypeLatticeElement.INT, arityRegister, arity));
-      add(builder -> builder.addInvoke(Type.DIRECT, lambdaInitializer, lambdaInitializer.proto,
-          Lists.newArrayList(ValueType.OBJECT, ValueType.INT),
-          Lists.newArrayList(receiverRegister, arityRegister)));
+      add(
+          builder ->
+              builder.addInvoke(
+                  Type.DIRECT,
+                  lambdaInitializer,
+                  lambdaInitializer.proto,
+                  Lists.newArrayList(ValueType.OBJECT, ValueType.INT),
+                  Lists.newArrayList(receiverRegister, arityRegister),
+                  false /* isInterface */));
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
index 82acf49..bcd0a75 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
@@ -85,16 +85,18 @@
       offsets[i] = nextInstructionIndex();
 
       // Emit fake call on `this` receiver.
-      add(builder -> {
-        if (arguments.isEmpty()) {
-          // Late initialization of argument list.
-          arguments.add(getReceiverValue());
-          for (int index = 0; index < paramCount; index++) {
-            arguments.add(getParamValue(index));
-          }
-        }
-        builder.addInvoke(Type.VIRTUAL, impl.method, impl.method.proto, arguments);
-      });
+      add(
+          builder -> {
+            if (arguments.isEmpty()) {
+              // Late initialization of argument list.
+              arguments.add(getReceiverValue());
+              for (int index = 0; index < paramCount; index++) {
+                arguments.add(getParamValue(index));
+              }
+            }
+            builder.addInvoke(
+                Type.VIRTUAL, impl.method, impl.method.proto, arguments, false /* isInterface */);
+          });
 
       // Handle return value if needed.
       if (returnsValue) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
index 37341fb..76dba16 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
@@ -23,6 +23,7 @@
   private final DexMethod target;
   private final Invoke.Type invokeType;
   private final boolean castResult;
+  private final boolean isInterface;
 
   public ForwardMethodSourceCode(
       DexType receiver,
@@ -31,7 +32,8 @@
       DexType targetReceiver,
       DexMethod target,
       Type invokeType,
-      Position callerPosition) {
+      Position callerPosition,
+      boolean isInterface) {
     this(
         receiver,
         method,
@@ -40,6 +42,7 @@
         target,
         invokeType,
         callerPosition,
+        isInterface,
         false);
   }
 
@@ -51,6 +54,7 @@
       DexMethod target,
       Type invokeType,
       Position callerPosition,
+      boolean isInterface,
       boolean castResult) {
     super(receiver, method, callerPosition, originalMethod);
     assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC);
@@ -58,6 +62,7 @@
     this.target = target;
     this.targetReceiver = targetReceiver;
     this.invokeType = invokeType;
+    this.isInterface = isInterface;
     this.castResult = castResult;
     assert checkSignatures();
 
@@ -119,8 +124,15 @@
     }
 
     // Method call to the target method.
-    add(builder -> builder.addInvoke(this.invokeType,
-        this.target, this.target.proto, argValueTypes, argRegisters));
+    add(
+        builder ->
+            builder.addInvoke(
+                this.invokeType,
+                this.target,
+                this.target.proto,
+                argValueTypes,
+                argRegisters,
+                this.isInterface));
 
     // Does the method return value?
     if (proto.returnType.isVoidType()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 60aec09..3ff88b9 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.optimize;
 
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -192,7 +191,7 @@
         findHolderForInterfaceMethodBridge(originalClass, targetClass.type);
     assert bridgeHolder != null;
     assert bridgeHolder != targetClass;
-    DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appInfo.dexItemFactory);
+    DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appInfo);
     bridgeHolder.addMethod(bridgeMethod);
     assert lookupTarget.apply(method) == bridgeMethod;
     return bridgeMethod;
@@ -243,8 +242,7 @@
       DexProgramClass bridgeHolder =
           findHolderForVisibilityBridge(originalClass, targetClass, packageDescriptor);
       assert bridgeHolder != null;
-      DexEncodedMethod bridgeMethod =
-          target.toForwardingMethod(bridgeHolder, appInfo.dexItemFactory);
+      DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appInfo);
       bridgeHolder.addMethod(bridgeMethod);
       assert lookupTarget.apply(method) == bridgeMethod;
       return bridgeMethod;
@@ -292,15 +290,14 @@
       Set<DexEncodedMethod> contexts = fieldsWithContexts.get(field);
       if (target != null && target.field != field
           && contexts.stream().allMatch(context ->
-              isVisibleFromOriginalContext(appInfo, context.method.getHolder(), target))) {
+              isVisibleFromOriginalContext(context.method.getHolder(), target))) {
         builder.map(field,
             lense.lookupField(validTargetFor(target.field, field, lookupTargetOnClass)));
       }
     }
   }
 
-  public static boolean isVisibleFromOriginalContext(
-      AppInfo appInfo, DexType context, DexEncodedField field) {
+  private boolean isVisibleFromOriginalContext(DexType context, DexEncodedField field) {
     DexType holderType = field.field.getHolder();
     DexClass holder = appInfo.definitionFor(holderType);
     if (holder == null) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index cabfb70..75bb98b 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1191,7 +1191,8 @@
               newMethod,
               graphLense.getOriginalMethodSignature(method.method),
               invocationTarget.method,
-              invocationTarget.isPrivateMethod() ? DIRECT : STATIC);
+              invocationTarget.isPrivateMethod() ? DIRECT : STATIC,
+              target.isInterface());
 
       // Add the bridge to the list of synthesized bridges such that the method signatures will
       // be updated by the end of vertical class merging.
@@ -1884,13 +1885,19 @@
     private DexMethod originalMethod;
     private DexMethod invocationTarget;
     private Type type;
+    private final boolean isInterface;
 
     public SynthesizedBridgeCode(
-        DexMethod method, DexMethod originalMethod, DexMethod invocationTarget, Type type) {
+        DexMethod method,
+        DexMethod originalMethod,
+        DexMethod invocationTarget,
+        Type type,
+        boolean isInterface) {
       this.method = method;
       this.originalMethod = originalMethod;
       this.invocationTarget = invocationTarget;
       this.type = type;
+      this.isInterface = isInterface;
     }
 
     // By the time the synthesized code object is created, vertical class merging still has not
@@ -1919,7 +1926,8 @@
               type == DIRECT ? method.holder : null,
               invocationTarget,
               type,
-              callerPosition);
+              callerPosition,
+              isInterface);
     }
 
     @Override
diff --git a/src/test/examples/autovalue/SimpleAutoValue.java b/src/test/examples/autovalue/SimpleAutoValue.java
deleted file mode 100644
index 2c0b2c6..0000000
--- a/src/test/examples/autovalue/SimpleAutoValue.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package autovalue;
-
-import com.google.auto.value.AutoValue;
-import javax.annotation.Nullable;
-
-public class SimpleAutoValue {
-
-  @AutoValue
-  static abstract class Pair {
-
-    Pair() {
-      // Intentionally left empty.
-    }
-
-    abstract int getOne();
-
-    @Nullable
-    abstract String getOther();
-
-    abstract String getRequiredOther();
-
-    static Builder builder() {
-      return new AutoValue_SimpleAutoValue_Pair.Builder();
-    }
-
-    @AutoValue.Builder
-    abstract static class Builder {
-
-      abstract Builder setOne(int value);
-
-      abstract Builder setOther(String value);
-
-      abstract Builder setRequiredOther(String value);
-
-      abstract Pair build();
-    }
-  }
-
-  public static void main(String... args) {
-    Pair.Builder builder = Pair.builder();
-    builder.setOne(42);
-    builder.setRequiredOther("123");
-    System.out.println(builder.build());
-    builder = Pair.builder();
-    try {
-      builder.build();
-    } catch (Exception e) {
-      System.out.println(e.getMessage());
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
index ea8ed0b..33f28fc 100644
--- a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
@@ -462,7 +462,7 @@
     main.addMainMethod(
         ".limit stack 2",
         ".limit locals 1",
-        "  getstatic Empty/aField I",
+        "  getstatic Empty/aMethod I",
         "  return");
 
     ensureRuntimeException(builder, NoSuchFieldError.class);
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java b/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
deleted file mode 100644
index ac1fd90..0000000
--- a/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.shaking;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.jasmin.JasminBuilder;
-import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
-import com.android.tools.r8.jasmin.JasminTestBase;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.util.Iterator;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class FieldReadsJasminTest extends JasminTestBase {
-  private static final String CLS = "Empty";
-  private static final String MAIN = "Main";
-  private final Backend backend;
-
-  @Parameterized.Parameters(name = "Backend: {0}")
-  public static Object[] data() {
-    return Backend.values();
-  }
-
-  public FieldReadsJasminTest(Backend backend) {
-    this.backend = backend;
-  }
-
-  @Test
-  public void testInstanceGet_nonNullReceiver() throws Exception {
-    JasminBuilder builder = new JasminBuilder();
-
-    ClassBuilder empty = builder.addClass(CLS);
-    empty.addField("protected", "aField", "I", null);
-
-    ClassBuilder main = builder.addClass(MAIN);
-    main.addMainMethod(
-        ".limit stack 2",
-        ".limit locals 1",
-        "  new Empty",
-        "  dup",
-        "  invokespecial Empty/<init>()V",
-        "  getfield Empty/aField I",
-        "  return");
-
-    ensureNoFields(builder, empty);
-  }
-
-  @Test
-  public void testStaticGet_noSideEffect() throws Exception {
-    JasminBuilder builder = new JasminBuilder();
-
-    ClassBuilder empty = builder.addClass(CLS);
-    empty.addStaticField("sField", "I");
-
-    ClassBuilder main = builder.addClass(MAIN);
-    main.addMainMethod(
-        ".limit stack 2",
-        ".limit locals 1",
-        "  getstatic Empty/sField I",
-        "  return");
-
-    ensureNoFields(builder, empty);
-  }
-
-  private void ensureNoFields(JasminBuilder app, ClassBuilder clazz) throws Exception {
-    testForR8(backend)
-        .addProgramClassFileData(app.buildClasses())
-        .addKeepRules("-keep class * { <methods>; }")
-        .compile()
-        .inspect(inspector -> {
-          ClassSubject classSubject = inspector.clazz(clazz.name);
-          assertThat(classSubject, isPresent());
-          classSubject.forAllFields(foundFieldSubject -> {
-            fail("Expect not to see any fields.");
-          });
-        });
-  }
-
-  @Test
-  public void testInstanceGet_nullableReceiver() throws Exception {
-    JasminBuilder builder = new JasminBuilder();
-
-    ClassBuilder empty = builder.addClass(CLS);
-    empty.addDefaultConstructor();
-    empty.addField("protected", "aField", "I", null);
-    MethodSignature foo = empty.addStaticMethod("foo", ImmutableList.of("L" + CLS + ";"), "V",
-        ".limit stack 2",
-        ".limit locals 1",
-        "  aload 0",
-        "  getfield Empty/aField I",
-        "  return");
-
-    ClassBuilder main = builder.addClass(MAIN);
-    main.addMainMethod(
-        ".limit stack 2",
-        ".limit locals 1",
-        "  aconst_null",
-        "  invokestatic Empty/foo(L" + CLS + ";)V",
-        "  return");
-
-    ensureFieldExists(builder, empty, foo, "aField");
-  }
-
-  @Test
-  public void testStaticGet_nonTrivialClinit() throws Exception {
-    JasminBuilder builder = new JasminBuilder();
-
-    ClassBuilder empty = builder.addClass(CLS);
-    empty.addDefaultConstructor();
-    empty.addStaticField("sField", "I");
-    empty.addClassInitializer(
-        ".limit stack 3",
-        ".limit locals 1",
-        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
-        "  ldc \"hello\"",
-        "  invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
-        "  return");
-
-    ClassBuilder main = builder.addClass(MAIN);
-    MethodSignature mainMethod = main.addMainMethod(
-        ".limit stack 2",
-        ".limit locals 1",
-        "  getstatic Empty/sField I",
-        "  return");
-
-    ensureFieldExists(builder, main, mainMethod, "sField");
-  }
-
-  @Test
-  public void testStaticGet_allocation() throws Exception {
-    JasminBuilder builder = new JasminBuilder();
-
-    ClassBuilder empty = builder.addClass(CLS);
-    empty.addDefaultConstructor();
-    empty.addStaticField("sField", "Ljava/lang/String;", "\"8\"");
-
-    ClassBuilder main = builder.addClass(MAIN);
-    MethodSignature mainMethod = main.addMainMethod(
-        ".limit stack 2",
-        ".limit locals 1",
-        "  getstatic Empty/sField Ljava/lang/String;",
-        "  return");
-
-    ensureFieldExists(builder, main, mainMethod, "sField");
-  }
-
-  private void ensureFieldExists(
-      JasminBuilder app, ClassBuilder clazz, MethodSignature method, String fieldName)
-      throws Exception {
-    testForR8(backend)
-        .addProgramClassFileData(app.buildClasses())
-        .addKeepRules("-keep class * { <methods>; }")
-        .compile()
-        .inspect(inspector -> {
-          ClassSubject classSubject = inspector.clazz(clazz.name);
-          assertThat(classSubject, isPresent());
-          MethodSubject methodSubject = classSubject.uniqueMethodWithName(method.name);
-          assertThat(methodSubject, isPresent());
-          Iterator<InstructionSubject> it =
-              methodSubject.iterateInstructions(InstructionSubject::isFieldAccess);
-          assertTrue(it.hasNext());
-          assertEquals(
-              inspector.clazz(CLS).uniqueFieldWithName(fieldName).getFinalName(),
-              it.next().getField().name.toString());
-        });
-  }
-
-}
diff --git a/third_party/opensource_apps.tar.gz.sha1 b/third_party/opensource_apps.tar.gz.sha1
new file mode 100644
index 0000000..9fb2bb2
--- /dev/null
+++ b/third_party/opensource_apps.tar.gz.sha1
@@ -0,0 +1 @@
+64b0689bce8f6789320b34da4e91c6dbcc1296e9
\ No newline at end of file
diff --git a/tools/download_all_benchmark_dependencies.py b/tools/download_all_benchmark_dependencies.py
new file mode 100755
index 0000000..43ec0ab
--- /dev/null
+++ b/tools/download_all_benchmark_dependencies.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+# Copyright (c) 2019, 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.
+
+# Utility script to make it easier to update what golem builds.
+
+import gradle
+import sys
+import utils
+
+BUILD_TARGETS = ['downloadDeps', 'downloadAndroidCts', 'downloadDx']
+
+def Main():
+  gradle.RunGradle(BUILD_TARGETS)
+  # Download opensource_apps and place in build.
+  utils.DownloadFromX20(utils.OPENSOURCE_APPS_SHA_FILE)
+
+
+if __name__ == '__main__':
+  sys.exit(Main())
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 105e958..55c8857 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -5,6 +5,7 @@
 
 import apk_masseur
 import apk_utils
+import golem
 import gradle
 import os
 import optparse
@@ -17,15 +18,20 @@
 
 import as_utils
 
-SHRINKERS = ['r8', 'r8-minified', 'r8full', 'r8full-minified', 'proguard']
-WORKING_DIR = utils.BUILD
+SHRINKERS = ['r8', 'r8-full', 'r8-nolib', 'r8-nolib-full', 'pg']
+WORKING_DIR = os.path.join(utils.BUILD, 'opensource_apps')
 
-if 'R8_BENCHMARK_DIR' in os.environ and os.path.isdir(os.environ['R8_BENCHMARK_DIR']):
+if ('R8_BENCHMARK_DIR' in os.environ
+    and os.path.isdir(os.environ['R8_BENCHMARK_DIR'])):
   WORKING_DIR = os.environ['R8_BENCHMARK_DIR']
 
+# For running on Golem all APPS are bundled as an x20-dependency and then copied
+# to WORKING_DIR. To make it easier to update the app-bundle, remove the folder
+# WORKING_DIR and then run run_on_as_app.py --download-only.
 APPS = {
   # 'app-name': {
   #     'git_repo': ...
+  #     'revision': ...,
   #     'app_module': ... (default app)
   #     'archives_base_name': ... (default same as app_module)
   #     'flavor': ... (default no flavor)
@@ -34,6 +40,7 @@
   'AnExplorer': {
       'app_id': 'dev.dworks.apps.anexplorer.pro',
       'git_repo': 'https://github.com/christofferqa/AnExplorer',
+      'revision': '365927477b8eab4052a1882d5e358057ae3dee4d',
       'flavor': 'googleMobilePro',
       'signed-apk-name': 'AnExplorer-googleMobileProRelease-4.0.3.apk',
       'min_sdk': 17
@@ -41,6 +48,7 @@
   'AntennaPod': {
       'app_id': 'de.danoeh.antennapod',
       'git_repo': 'https://github.com/christofferqa/AntennaPod.git',
+      'revision': '77e94f4783a16abe9cc5b78dc2d2b2b1867d8c06',
       'flavor': 'play',
       'min_sdk': 14,
       'compile_sdk': 26
@@ -48,58 +56,70 @@
   'apps-android-wikipedia': {
       'app_id': 'org.wikipedia',
       'git_repo': 'https://github.com/christofferqa/apps-android-wikipedia',
+      'revision': '686e8aa5682af8e6a905054b935dd2daa57e63ee',
       'flavor': 'prod',
-      'signed-apk-name': 'app-prod-universal-release.apk'
+      'signed-apk-name': 'app-prod-universal-release.apk',
   },
   'chanu': {
-    'app_id': 'com.chanapps.four.activity',
-    'git_repo': 'https://github.com/mkj-gram/chanu.git',
+      'app_id': 'com.chanapps.four.activity',
+      'git_repo': 'https://github.com/mkj-gram/chanu.git',
+      'revision': '04ade1e9c33d707f0850d5eb9d6fa5e8af814a26',
   },
   'friendlyeats-android': {
       'app_id': 'com.google.firebase.example.fireeats',
-      'git_repo': 'https://github.com/christofferqa/friendlyeats-android.git'
+      'git_repo': 'https://github.com/christofferqa/friendlyeats-android.git',
+      'revision': '10091fa0ec37da12e66286559ad1b6098976b07b',
   },
   'Instabug-Android': {
       'app_id': 'com.example.instabug',
       'git_repo': 'https://github.com/christofferqa/Instabug-Android.git',
+      'revision': 'b8df78c96630a6537fbc07787b4990afc030cc0f'
   },
   'KISS': {
       'app_id': 'fr.neamar.kiss',
       'git_repo': 'https://github.com/christofferqa/KISS',
+      'revision': '093da9ee0512e67192f62951c45a07a616fc3224',
   },
   'materialistic': {
       'app_id': 'io.github.hidroh.materialistic',
       'git_repo': 'https://github.com/christofferqa/materialistic',
+      'revision': '2b2b2ee25ce9e672d5aab1dc90a354af1522b1d9',
   },
   'Minimal-Todo': {
       'app_id': 'com.avjindersinghsekhon.minimaltodo',
       'git_repo': 'https://github.com/christofferqa/Minimal-Todo',
+      'revision': '9d8c73746762cd376b718858ec1e8783ca07ba7c',
   },
   'NewPipe': {
       'app_id': 'org.schabi.newpipe',
       'git_repo': 'https://github.com/christofferqa/NewPipe',
+      'revision': 'ed543099c7823be00f15d9340f94bdb7cb37d1e6',
   },
   'rover-android': {
-    'app_id': 'io.rover.app.debug',
-    'app_module': 'debug-app',
-    'git_repo': 'https://github.com/mkj-gram/rover-android.git',
+      'app_id': 'io.rover.app.debug',
+      'app_module': 'debug-app',
+      'git_repo': 'https://github.com/mkj-gram/rover-android.git',
+      'revision': 'd2e876e597b3af7eab406e38a0e08327a38bd942',
   },
   'Signal-Android': {
-    'app_id': 'org.thoughtcrime.securesms',
-    'app_module': '',
-    'flavor': 'play',
-    'git_repo': 'https://github.com/mkj-gram/Signal-Android.git',
-    'releaseTarget': 'assemblePlayRelease',
-    'signed-apk-name': 'Signal-play-release-4.32.7.apk',
+      'app_id': 'org.thoughtcrime.securesms',
+      'app_module': '',
+      'flavor': 'play',
+      'git_repo': 'https://github.com/mkj-gram/Signal-Android.git',
+      'revision': '85e1a10993e5e9ffe923f0798b26cbc44068ba31',
+      'releaseTarget': 'assemblePlayRelease',
+      'signed-apk-name': 'Signal-play-release-4.32.7.apk',
   },
   'Simple-Calendar': {
       'app_id': 'com.simplemobiletools.calendar.pro',
       'git_repo': 'https://github.com/christofferqa/Simple-Calendar',
+      'revision': '82dad8c203eea5a0f0ddb513506d8f1de986ef2b',
       'signed-apk-name': 'calendar-release.apk'
   },
   'sqldelight': {
       'app_id': 'com.example.sqldelight.hockey',
       'git_repo': 'https://github.com/christofferqa/sqldelight.git',
+      'revision': '2e67a1126b6df05e4119d1e3a432fde51d76cdc8',
       'app_module': 'sample/android',
       'archives_base_name': 'android',
       'min_sdk': 14,
@@ -108,6 +128,7 @@
   'tachiyomi': {
       'app_id': 'eu.kanade.tachiyomi',
       'git_repo': 'https://github.com/sgjesse/tachiyomi.git',
+      'revision': 'b15d2fe16864645055af6a745a62cc5566629798',
       'flavor': 'standard',
       'releaseTarget': 'app:assembleRelease',
       'min_sdk': 16
@@ -115,21 +136,25 @@
   'tivi': {
       'app_id': 'app.tivi',
       'git_repo': 'https://github.com/sgjesse/tivi.git',
+      'revision': '7d7f591d6f39d7caeb88dd13bf476c0c06accdfb',
       'min_sdk': 23,
       'compile_sdk': 28,
   },
   'Tusky': {
-    'app_id': 'com.keylesspalace.tusky',
-    'git_repo': 'https://github.com/mkj-gram/Tusky.git',
-    'flavor': 'blue'
+      'app_id': 'com.keylesspalace.tusky',
+      'git_repo': 'https://github.com/mkj-gram/Tusky.git',
+      'revision': 'b794f3ab90388add98461ffe70edb65c39351c33',
+      'flavor': 'blue'
   },
   'Vungle-Android-SDK': {
-    'app_id': 'com.publisher.vungle.sample',
-    'git_repo': 'https://github.com/mkj-gram/Vungle-Android-SDK.git',
+      'app_id': 'com.publisher.vungle.sample',
+      'git_repo': 'https://github.com/mkj-gram/Vungle-Android-SDK.git',
+      'revision': '3e231396ea7ce97b2655e03607497c75730e45f6',
   },
   # This does not build yet.
   'muzei': {
       'git_repo': 'https://github.com/sgjesse/muzei.git',
+      'revision': 'bed2a5f79c6e08b0a21e3e3f9242232d0848ef74',
       'app_module': 'main',
       'archives_base_name': 'muzei',
       'skip': True,
@@ -161,17 +186,21 @@
   return '~~R8' in subprocess.check_output(cmd).strip()
 
 def IsMinifiedR8(shrinker):
-  return shrinker == 'r8-minified' or shrinker == 'r8full-minified'
+  return 'nolib' not in shrinker
 
 def IsTrackedByGit(file):
   return subprocess.check_output(['git', 'ls-files', file]).strip() != ''
 
-def GitClone(git_url):
-  return subprocess.check_output(['git', 'clone', git_url]).strip()
-
-def GitPull():
-  # Use --no-edit to accept the auto-generated merge message, if any.
-  return subprocess.call(['git', 'pull', '--no-edit']) == 0
+def GitClone(git_url, revision, checkout_dir, options):
+  result = subprocess.check_output(
+      ['git', 'clone', git_url, checkout_dir]).strip()
+  head_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
+  if revision == head_rev:
+    return result
+  warn('Target revision is not head in {}.'.format(checkout_dir))
+  with utils.ChangedWorkingDirectory(checkout_dir, quiet=options.quiet):
+    subprocess.check_output(['git', 'reset', '--hard', revision])
+  return result
 
 def GitCheckout(file):
   return subprocess.check_output(['git', 'checkout', file]).strip()
@@ -230,26 +259,23 @@
       return True
 
 def GetResultsForApp(app, config, options, temp_dir):
-  git_repo = config['git_repo']
-
   # Checkout and build in the build directory.
   checkout_dir = os.path.join(WORKING_DIR, app)
 
   result = {}
 
-  if not os.path.exists(checkout_dir):
+  if not os.path.exists(checkout_dir) and not options.golem:
     with utils.ChangedWorkingDirectory(WORKING_DIR, quiet=options.quiet):
-      GitClone(git_repo)
-  elif options.pull:
-    with utils.ChangedWorkingDirectory(checkout_dir, quiet=options.quiet):
-      # Checkout build.gradle to avoid merge conflicts.
-      if IsTrackedByGit('build.gradle'):
-        GitCheckout('build.gradle')
+      GitClone(config['git_repo'], config['revision'], checkout_dir, options)
 
-      if not GitPull():
-        result['status'] = 'failed'
-        result['error_message'] = 'Unable to pull from remote'
-        return result
+  checkout_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
+  if config['revision'] != checkout_rev:
+    msg = 'Checkout is not target revision for {} in {}.'.format(
+        app, checkout_dir)
+    if options.ignore_versions:
+      warn(msg)
+    else:
+      raise Exception(msg)
 
   result['status'] = 'success'
 
@@ -277,7 +303,7 @@
             BuildAppWithShrinker(app, config, shrinker, checkout_dir, out_dir,
                 temp_dir, options)
         dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
-        result['apk_dest'] = apk_dest,
+        result['apk_dest'] = apk_dest
         result['build_status'] = 'success'
         result['dex_size'] = dex_size
         result['profile_dest_dir'] = profile_dest_dir
@@ -367,11 +393,8 @@
       shrinker,
       ' for recompilation' if keepRuleSynthesisForRecompilation else ''))
 
-  # Add/remove 'r8.jar' from top-level build.gradle.
-  if options.disable_tot:
-    as_utils.remove_r8_dependency(checkout_dir)
-  else:
-    as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))
+  # Add 'r8.jar' from top-level build.gradle.
+  as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))
 
   app_module = config.get('app_module', 'app')
   archives_base_name = config.get('archives_base_name', app_module)
@@ -398,7 +421,7 @@
   # Value for property android.enableR8.
   enableR8 = 'r8' in shrinker
   # Value for property android.enableR8.fullMode.
-  enableR8FullMode = shrinker == 'r8full' or shrinker == 'r8full-minified'
+  enableR8FullMode = shrinker == 'r8-full' or shrinker == 'r8-nolib-full'
   # Build gradlew command line.
   cmd = ['./gradlew', '--no-daemon', 'clean', releaseTarget,
          '--profile', '--stacktrace',
@@ -516,10 +539,28 @@
 def LogResultsForApps(result_per_shrinker_per_app, options):
   print('')
   for app, result_per_shrinker in sorted(
-      result_per_shrinker_per_app.iteritems()):
+      result_per_shrinker_per_app.iteritems(), key=lambda s: s[0].lower()):
     LogResultsForApp(app, result_per_shrinker, options)
 
 def LogResultsForApp(app, result_per_shrinker, options):
+  if options.print_dexsegments:
+    LogSegmentsForApp(app, result_per_shrinker, options)
+  else:
+    LogComparisonResultsForApp(app, result_per_shrinker, options)
+
+def LogSegmentsForApp(app, result_per_shrinker, options):
+  for shrinker in SHRINKERS:
+    if shrinker not in result_per_shrinker:
+      continue
+    result = result_per_shrinker[shrinker];
+    benchmark_name = '{}-{}'.format(options.print_dexsegments, app)
+    utils.print_dexsegments(benchmark_name, [result.get('apk_dest')])
+    duration = sum(result.get('profile').values())
+    print('%s-Total(RunTimeRaw): %s ms' % (benchmark_name, duration * 1000))
+    print('%s-Total(CodeSize): %s' % (benchmark_name, result.get('dex_size')))
+
+
+def LogComparisonResultsForApp(app, result_per_shrinker, options):
   print(app + ':')
 
   if result_per_shrinker.get('status', 'success') != 'success':
@@ -527,7 +568,7 @@
     print('  skipped ({})'.format(error_message))
     return
 
-  proguard_result = result_per_shrinker.get('proguard', {})
+  proguard_result = result_per_shrinker.get('pg', {})
   proguard_dex_size = float(proguard_result.get('dex_size', -1))
   proguard_duration = sum(proguard_result.get('profile', {}).values())
 
@@ -596,6 +637,21 @@
   result.add_option('--app',
                     help='What app to run on',
                     choices=APPS.keys())
+  result.add_option('--download-only', '--download_only',
+                    help='Whether to download apps without any compilation',
+                    default=False,
+                    action='store_true')
+  result.add_option('--golem',
+                    help='Running on golem, do not download',
+                    default=False,
+                    action='store_true')
+  result.add_option('--gradle-flags', '--gradle_flags',
+                    help='Flags to pass in to gradle')
+  result.add_option('--ignore-versions', '--ignore_versions',
+                    help='Allow checked-out app to differ in revision from '
+                         'pinned',
+                    default=False,
+                    action='store_true')
   result.add_option('--keystore',
                     help='Path to app.keystore',
                     default='app.keystore')
@@ -610,10 +666,23 @@
                     help='Number of events that the monkey should trigger',
                     default=250,
                     type=int)
-  result.add_option('--pull',
-                    help='Whether to pull the latest version of each app',
+  result.add_option('--no-build', '--no_build',
+                    help='Run without building ToT first (only when using ToT)',
                     default=False,
                     action='store_true')
+  result.add_option('--quiet',
+                    help='Disable verbose logging',
+                    default=False,
+                    action='store_true')
+  result.add_option('--print-dexsegments',
+                    metavar='BENCHMARKNAME',
+                    help='Print the sizes of individual dex segments as ' +
+                         '\'<BENCHMARKNAME>-<APP>-<segment>(CodeSize): '
+                         '<bytes>\'')
+  result.add_option('--r8-compilation-steps', '--r8_compilation_steps',
+                    help='Number of times R8 should be run on each app',
+                    default=2,
+                    type=int)
   result.add_option('--sign-apks', '--sign_apks',
                     help='Whether the APKs should be signed',
                     default=False,
@@ -621,57 +690,50 @@
   result.add_option('--shrinker',
                     help='The shrinkers to use (by default, all are run)',
                     action='append')
-  result.add_option('--r8-compilation-steps', '--r8_compilation_steps',
-                    help='Number of times R8 should be run on each app',
-                    default=2,
-                    type=int)
-  result.add_option('--disable-tot', '--disable_tot',
-                    help='Whether to disable the use of the ToT version of R8',
-                    default=False,
-                    action='store_true')
-  result.add_option('--no-build', '--no_build',
-                    help='Run without building ToT first (only when using ToT)',
-                    default=False,
-                    action='store_true')
-  result.add_option('--gradle-flags', '--gradle_flags',
-                    help='Flags to pass in to gradle')
-  result.add_option('--quiet',
-                    help='Disable verbose logging',
-                    default=False,
-                    action='store_true')
   (options, args) = result.parse_args(argv)
-  if options.disable_tot:
-    # r8.jar is required for recompiling the generated APK
-    options.r8_compilation_steps = 1
   if options.shrinker:
     for shrinker in options.shrinker:
       assert shrinker in SHRINKERS
   return (options, args)
 
+def download_apps(options):
+  # Download apps and place in build
+  with utils.ChangedWorkingDirectory(WORKING_DIR):
+    for app, config in APPS.iteritems():
+      app_dir = os.path.join(WORKING_DIR, app)
+      if not os.path.exists(app_dir):
+        GitClone(config['git_repo'], config['revision'], app_dir, options)
+
+
 def main(argv):
   global SHRINKERS
 
   (options, args) = ParseOptions(argv)
 
+  if options.golem:
+    golem.link_third_party()
+    if os.path.exists(WORKING_DIR):
+      shutil.rmtree(WORKING_DIR)
+    shutil.copytree(utils.OPENSOURCE_APPS_FOLDER, WORKING_DIR)
+
+  if not os.path.exists(WORKING_DIR):
+    os.makedirs(WORKING_DIR)
+
+  if options.download_only:
+    download_apps(options)
+    return
+
   with utils.TempDir() as temp_dir:
-    if options.disable_tot:
-      # Cannot run r8 lib without adding r8lib.jar as an dependency
-      SHRINKERS = [
-          shrinker for shrinker in SHRINKERS
-          if 'minified' not in shrinker]
-    else:
-      if not options.no_build:
-        gradle.RunGradle(['r8', 'r8lib'])
+    if not options.no_build or options.golem:
+      gradle.RunGradle(['r8', 'r8lib'])
 
-      assert os.path.isfile(utils.R8_JAR), (
-          'Cannot build from ToT without r8.jar')
-      assert os.path.isfile(utils.R8LIB_JAR), (
-          'Cannot build from ToT without r8lib.jar')
+    assert os.path.isfile(utils.R8_JAR), 'Cannot build without r8.jar'
+    assert os.path.isfile(utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
 
-      # Make a copy of r8.jar and r8lib.jar such that they stay the same for
-      # the entire execution of this script.
-      shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
-      shutil.copyfile(utils.R8LIB_JAR, os.path.join(temp_dir, 'r8lib.jar'))
+    # Make a copy of r8.jar and r8lib.jar such that they stay the same for
+    # the entire execution of this script.
+    shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
+    shutil.copyfile(utils.R8LIB_JAR, os.path.join(temp_dir, 'r8lib.jar'))
 
     result_per_shrinker_per_app = {}
 
@@ -679,7 +741,7 @@
       result_per_shrinker_per_app[options.app] = GetResultsForApp(
           options.app, APPS.get(options.app), options, temp_dir)
     else:
-      for app, config in sorted(APPS.iteritems()):
+      for app, config in sorted(APPS.iteritems(), key=lambda s: s[0].lower()):
         if not config.get('skip', False):
           result_per_shrinker_per_app[app] = GetResultsForApp(
               app, config, options, temp_dir)
diff --git a/tools/utils.py b/tools/utils.py
index aff96a3..08faec6 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -62,6 +62,10 @@
 CF_SEGMENTS_TOOL = os.path.join(THIRD_PARTY, 'cf_segments')
 PINNED_R8_JAR = os.path.join(REPO_ROOT, 'third_party/r8/r8.jar')
 PINNED_PGR8_JAR = os.path.join(REPO_ROOT, 'third_party/r8/r8-pg6.0.1.jar')
+OPENSOURCE_APPS_SHA_FILE = os.path.join(
+    REPO_ROOT,
+    'third_party/opensource_apps.tar.gz.sha1')
+OPENSOURCE_APPS_FOLDER = os.path.join(REPO_ROOT, 'third_party/opensource_apps/')
 
 
 # Common environment setup.
@@ -184,9 +188,12 @@
   return 'origin/master' in remotes
 
 def get_HEAD_sha1():
+  return get_HEAD_sha1_for_checkout(REPO_ROOT)
+
+def get_HEAD_sha1_for_checkout(checkout):
   cmd = ['git', 'rev-parse', 'HEAD']
   PrintCmd(cmd)
-  with ChangedWorkingDirectory(REPO_ROOT):
+  with ChangedWorkingDirectory(checkout):
     return subprocess.check_output(cmd).strip()
 
 def makedirs_if_needed(path):