Extend inlining to allow invokes.

Also extend the oracle to hand package private and protected access modifiers
correctly.

Bug:
Change-Id: I1b592660842d4d81dd508e69f7c5641860f11633
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 84d6d5d..54f9470 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -33,6 +33,10 @@
     return false;
   }
 
+  public int estimatedSizeForInlining() {
+    return Integer.MAX_VALUE;
+  }
+
   public DexCode asDexCode() {
     throw new Unreachable(getClass().getCanonicalName() + ".asDexCode()");
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index aedb6df..04c61a4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -7,9 +7,7 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
-
 import com.google.common.base.MoreObjects;
-
 import java.util.Arrays;
 import java.util.function.Consumer;
 
@@ -204,4 +202,9 @@
     // For non-dex code we don't try to check the code.
     return true;
   }
+
+  public boolean defaultValuesForStaticFieldsMayTriggerAllocation() {
+    return Arrays.stream(staticFields())
+        .anyMatch(field -> !field.staticValue.mayTriggerAllocation());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index d939868..dc3ae37 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -64,6 +64,11 @@
   }
 
   @Override
+  public int estimatedSizeForInlining() {
+    return instructions.length;
+  }
+
+  @Override
   public DexCode asDexCode() {
     return this;
   }
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 400b42f..ef6a96c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -3,9 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE;
-import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_PRIVATE;
-import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_PUBLIC;
+import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_ANY;
+import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SAME_CLASS;
+import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE;
+import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SUBCLASS;
 import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
 
 import com.android.tools.r8.code.Const;
@@ -35,17 +36,40 @@
 
 public class DexEncodedMethod extends KeyedDexItem<DexMethod> {
 
-  public enum CompilationState
-
-  {
+  /**
+   * Encodes the processing state of a method.
+   * <p>
+   * We also use this enum to encode whether and if under what constraints a method may be
+   * inlined.
+   */
+  public enum CompilationState {
+    /**
+     * Has not been processed, yet.
+     */
     NOT_PROCESSED,
+    /**
+     * Has been processed but cannot be inlined due to instructions that are not supported.
+     */
     PROCESSED_NOT_INLINING_CANDIDATE,
-    // Code only contains instructions that access public entities.
-    PROCESSED_INLINING_CANDIDATE_PUBLIC,
-    // Code only contains instructions that access public and package private entities.
-    PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE,
-    // Code also contains instructions that access public entities.
-    PROCESSED_INLINING_CANDIDATE_PRIVATE,
+    /**
+     * Code only contains instructions that access public entities and can this be inlined
+     * into any context.
+     */
+    PROCESSED_INLINING_CANDIDATE_ANY,
+    /**
+     * Code also contains instructions that access protected entities that reside in a differnt
+     * package and hence require subclass relationship to be visible.
+     */
+    PROCESSED_INLINING_CANDIDATE_SUBCLASS,
+    /**
+     * Code contains instructions that reference package private entities or protected entities
+     * from the same package.
+     */
+    PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE,
+    /**
+     * Code contains instructions that reference private entities.
+     */
+    PROCESSED_INLINING_CANDIDATE_SAME_CLASS,
   }
 
   public static final DexEncodedMethod[] EMPTY_ARRAY = new DexEncodedMethod[]{};
@@ -79,7 +103,8 @@
         || compilationState == CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
   }
 
-  public boolean isInliningCandidate(DexEncodedMethod container, boolean alwaysInline) {
+  public boolean isInliningCandidate(DexEncodedMethod container, boolean alwaysInline,
+      AppInfoWithSubtyping appInfo) {
     if (container.accessFlags.isStatic() && container.accessFlags.isConstructor()) {
       // This will probably never happen but never inline a class initializer.
       return false;
@@ -92,33 +117,33 @@
       return true;
     }
     switch (compilationState) {
-      case PROCESSED_INLINING_CANDIDATE_PUBLIC:
+      case PROCESSED_INLINING_CANDIDATE_ANY:
         return true;
-      case PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE:
+      case PROCESSED_INLINING_CANDIDATE_SUBCLASS:
+        return container.method.getHolder().isSubtypeOf(method.getHolder(), appInfo);
+      case PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE:
         return container.method.getHolder().isSamePackage(method.getHolder());
-      // TODO(bak): Expand check for package private access:
-      case PROCESSED_INLINING_CANDIDATE_PRIVATE:
+      case PROCESSED_INLINING_CANDIDATE_SAME_CLASS:
         return container.method.getHolder() == method.getHolder();
       default:
         return false;
     }
   }
 
-  public boolean isPublicInlining() {
-    return compilationState == PROCESSED_INLINING_CANDIDATE_PUBLIC;
-  }
-
   public boolean markProcessed(Constraint state) {
     CompilationState prevCompilationState = compilationState;
     switch (state) {
       case ALWAYS:
-        compilationState = PROCESSED_INLINING_CANDIDATE_PUBLIC;
+        compilationState = PROCESSED_INLINING_CANDIDATE_ANY;
+        break;
+      case SUBCLASS:
+        compilationState = PROCESSED_INLINING_CANDIDATE_SUBCLASS;
         break;
       case PACKAGE:
-        compilationState = PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE;
+        compilationState = PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE;
         break;
-      case PRIVATE:
-        compilationState = PROCESSED_INLINING_CANDIDATE_PRIVATE;
+      case SAMECLASS:
+        compilationState = PROCESSED_INLINING_CANDIDATE_SAME_CLASS;
         break;
       case NEVER:
         compilationState = PROCESSED_NOT_INLINING_CANDIDATE;
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 29f59e5..08c8a1c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -99,6 +99,15 @@
     return this == defaultForType(type, factory);
   }
 
+  /**
+   * Whether creating this value as a default value for a field might trigger an allocation.
+   * <p>
+   * This is conservative.
+   */
+  public boolean mayTriggerAllocation() {
+    return true;
+  }
+
   static private abstract class SimpleDexValue extends DexValue {
 
     @Override
@@ -111,6 +120,11 @@
       // Intentionally empty
     }
 
+    @Override
+    public boolean mayTriggerAllocation() {
+      return false;
+    }
+
     protected static void writeIntegerTo(byte type, long value, int expected,
         DexOutputBuffer dest) {
       // Leave space for header.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index d624990..c7b55d5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
@@ -67,7 +67,7 @@
   }
 
   @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     return Constraint.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
index 899516a..bcdfc22 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Binop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -7,7 +7,7 @@
 import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
 
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
@@ -112,7 +112,7 @@
   }
 
   @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     return Constraint.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java b/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
index 657faf7..8259926 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
@@ -3,6 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
+
 public abstract class ConstInstruction extends Instruction {
 
   public ConstInstruction(Value out) {
@@ -23,4 +27,9 @@
   public ConstInstruction asConstInstruction() {
     return this;
   }
+
+  @Override
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
+    return Constraint.ALWAYS;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index 0da5b43..d53c577 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -12,10 +12,7 @@
 import com.android.tools.r8.code.ConstWide32;
 import com.android.tools.r8.code.ConstWideHigh16;
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.utils.NumberUtils;
 
 public class ConstNumber extends ConstInstruction {
@@ -186,9 +183,4 @@
   public ConstNumber asConstNumber() {
     return this;
   }
-
-  @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
-    return Constraint.ALWAYS;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 065d3df..ca18978 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
@@ -44,22 +44,17 @@
   abstract DexEncodedField lookupTarget(DexType type, AppInfo appInfo);
 
   @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     // Resolve the field if possible and decide whether the instruction can inlined.
     DexType fieldHolder = field.getHolder();
     DexEncodedField target = lookupTarget(fieldHolder, info);
     DexClass fieldClass = info.definitionFor(fieldHolder);
-    if ((target != null) && (fieldClass != null) && !fieldClass.isLibraryClass()) {
-      DexAccessFlags flags = target.accessFlags;
-      if (flags.isPublic()) {
-        return Constraint.ALWAYS;
-      }
-      if (flags.isPrivate() && (fieldHolder == holder)) {
-        return Constraint.PRIVATE;
-      }
-      if (flags.isProtected() && (fieldHolder.isSamePackage(holder))) {
-        return Constraint.PACKAGE;
-      }
+    if ((target != null) && (fieldClass != null)) {
+      Constraint fieldConstraint = Constraint
+          .deriveConstraint(holder, fieldHolder, target.accessFlags, info);
+      Constraint classConstraint = Constraint
+          .deriveConstraint(holder, fieldHolder, fieldClass.accessFlags, info);
+      return Constraint.min(fieldConstraint, classConstraint);
     }
     return Constraint.NEVER;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
index 1126299..955a6cc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -75,14 +75,11 @@
   }
 
   @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     DexClass targetClass = info.definitionFor(type());
     if (targetClass == null) {
       return Constraint.NEVER;
     }
-    if (targetClass.accessFlags.isPublic()) {
-      return Constraint.ALWAYS;
-    }
-    return Constraint.NEVER;
+    return Constraint.deriveConstraint(holder, type(), targetClass.accessFlags, info);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 083100b..e6ba6c0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.Value.DebugInfo;
@@ -839,7 +839,7 @@
   }
 
   // Returns the inlining constraint for this instruction.
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     return Constraint.NEVER;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index f11d62f..cc23ef5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -5,12 +5,11 @@
 
 import com.android.tools.r8.code.InvokeDirectRange;
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
-import com.android.tools.r8.ir.optimize.InliningOracle;
 import java.util.List;
 
 public class InvokeDirect extends InvokeMethodWithReceiver {
@@ -89,7 +88,7 @@
   }
 
   @Override
-  public InlineAction computeInlining(InliningOracle decider) {
-    return decider.computeForInvokeDirect(this);
+  DexEncodedMethod lookupTarget(AppInfo appInfo) {
+    return appInfo.lookupDirectTarget(getInvokedMethod());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 9d0c01f..a639ba1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -4,12 +4,11 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
-import com.android.tools.r8.ir.optimize.InliningOracle;
 import java.util.List;
 
 public class InvokeInterface extends InvokeMethodWithReceiver {
@@ -76,7 +75,8 @@
   }
 
   @Override
-  public InlineAction computeInlining(InliningOracle decider) {
-    return decider.computeForInvokeInterface(this);
+  DexEncodedMethod lookupTarget(AppInfo appInfo) {
+    DexMethod method = getInvokedMethod();
+    return appInfo.lookupVirtualDefinition(method.holder, method);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 950e31f..70201f0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -3,7 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
 import com.android.tools.r8.ir.optimize.InliningOracle;
 import java.util.List;
@@ -46,5 +52,28 @@
     return this;
   }
 
+  abstract DexEncodedMethod lookupTarget(AppInfo appInfo);
+
+  @Override
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
+    if (method.holder.isArrayType()) {
+      return Constraint.ALWAYS;
+    }
+    DexEncodedMethod target = lookupTarget(info);
+    if (target != null) {
+      DexType methodHolder = target.method.holder;
+      DexClass methodClass = info.definitionFor(methodHolder);
+      if ((methodClass != null)) {
+        Constraint methodConstrain = Constraint
+            .deriveConstraint(holder, methodHolder, target.accessFlags, info);
+        // We also have to take the constraint of the enclosing class into account.
+        Constraint classConstraint = Constraint
+            .deriveConstraint(holder, methodHolder, methodClass.accessFlags, info);
+        return Constraint.min(methodConstrain, classConstraint);
+      }
+    }
+    return Constraint.NEVER;
+  }
+
   public abstract InlineAction computeInlining(InliningOracle decider);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index c83b1d2..cdc8644 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
 import java.util.List;
 
 public abstract class InvokeMethodWithReceiver extends InvokeMethod {
@@ -31,4 +33,10 @@
   public InvokeMethodWithReceiver asInvokeMethodWithReceiver() {
     return this;
   }
+
+
+  @Override
+  public final InlineAction computeInlining(InliningOracle decider) {
+    return decider.computeForInvokeWithReceiver(this);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index efc12db..5eacaa9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.code.InvokePolymorphicRange;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
@@ -87,6 +88,12 @@
   }
 
   @Override
+  DexEncodedMethod lookupTarget(AppInfo appInfo) {
+    // TODO(herhut): Implement lookup target for invokePolymorphic.
+    return null;
+  }
+
+  @Override
   public InlineAction computeInlining(InliningOracle decider) {
     return decider.computeForInvokePolymorpic(this);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index dc22e2e..6654c16 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.code.InvokeStaticRange;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
@@ -76,6 +77,12 @@
   }
 
   @Override
+  DexEncodedMethod lookupTarget(AppInfo appInfo) {
+    DexMethod method = getInvokedMethod();
+    return appInfo.lookupStaticTarget(method);
+  }
+
+  @Override
   public InlineAction computeInlining(InliningOracle decider) {
     return decider.computeForInvokeStatic(this);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index fc79c11..b3d5fe4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -4,15 +4,16 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.code.InvokeSuperRange;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
-import com.android.tools.r8.ir.optimize.InliningOracle;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import java.util.List;
 
-public class InvokeSuper extends InvokeMethod {
+public class InvokeSuper extends InvokeMethodWithReceiver {
 
   public InvokeSuper(DexMethod target, Value result, List<Value> arguments) {
     super(target, result, arguments);
@@ -75,7 +76,14 @@
   }
 
   @Override
-  public InlineAction computeInlining(InliningOracle decider) {
-    return decider.computeForInvokeSuper(this);
+  DexEncodedMethod lookupTarget(AppInfo appInfo) {
+    DexMethod method = getInvokedMethod();
+    return appInfo.lookupVirtualDefinition(method.holder, method);
+  }
+
+  @Override
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
+    // The semantics of invoke super depend on the context.
+    return Constraint.SAMECLASS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 3988980..34793c2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -4,12 +4,11 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.code.InvokeVirtualRange;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
-import com.android.tools.r8.ir.optimize.InliningOracle;
 import java.util.List;
 
 public class InvokeVirtual extends InvokeMethodWithReceiver {
@@ -76,7 +75,8 @@
   }
 
   @Override
-  public InlineAction computeInlining(InliningOracle decider) {
-    return decider.computeForInvokeVirtual(this);
+  DexEncodedMethod lookupTarget(AppInfo appInfo) {
+    DexMethod method = getInvokedMethod();
+    return appInfo.lookupVirtualTarget(method.holder, method);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java b/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
index ec9c12f..9d35ce2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.utils.InternalOptions;
@@ -47,7 +47,7 @@
   }
 
   @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     return Constraint.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java
index 91e19f0..277c63c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Move.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
@@ -81,7 +81,7 @@
   }
 
   @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     return Constraint.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index 142206c..337ed71 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.code.ReturnWide;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
@@ -110,7 +110,7 @@
   }
 
   @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     return Constraint.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
index 9bd6a81..2f9f9f7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Throw.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
@@ -63,7 +63,7 @@
   }
 
   @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     return Constraint.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Unop.java b/src/main/java/com/android/tools/r8/ir/code/Unop.java
index 174b7b6..a534420 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Unop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Unop.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 
@@ -43,7 +43,7 @@
   }
 
   @Override
-  public Constraint inliningConstraint(AppInfo info, DexType holder) {
+  public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
     return Constraint.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 51aa284..e6b592d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -6,8 +6,6 @@
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
 
-import com.google.common.collect.ImmutableSet;
-
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -39,7 +37,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
-
+import com.google.common.collect.ImmutableSet;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -511,7 +509,7 @@
     if (!options.inlineAccessors || inliner == null) {
       state = Constraint.NEVER;
     } else {
-      state = inliner.identifySimpleMethods(code, method);
+      state = inliner.computeInliningConstraint(code, method);
     }
     feedback.markProcessed(method, state);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index a2b3ba2..876c83b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -4,8 +4,12 @@
 package com.android.tools.r8.ir.optimize;
 
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexAccessFlags;
+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;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -34,8 +38,6 @@
 
 public class Inliner {
 
-  private static final int INLINING_INSTRUCTION_LIMIT = 5;
-
   protected final AppInfoWithSubtyping appInfo;
   private final GraphLense graphLense;
   private final InternalOptions options;
@@ -61,50 +63,43 @@
     return result;
   }
 
-  public Constraint identifySimpleMethods(IRCode code, DexEncodedMethod method) {
-    DexCode dex = method.getCode().asDexCode();
-    // We have generated code for a method and we want to figure out whether the method is a
-    // candidate for inlining. The code is the final IR after optimizations.
-    if (dex.instructions.length > INLINING_INSTRUCTION_LIMIT) {
-      return Constraint.NEVER;
-    }
+  public Constraint computeInliningConstraint(IRCode code, DexEncodedMethod method) {
     Constraint result = Constraint.ALWAYS;
-    ListIterator<BasicBlock> iterator = code.listIterator();
-    assert iterator.hasNext();
-    BasicBlock block = iterator.next();
-    BasicBlock nextBlock;
-    do {
-      nextBlock = iterator.hasNext() ? iterator.next() : null;
-      InstructionListIterator it = block.listIterator();
-      while (it.hasNext()) {
-        Instruction instruction = it.next();
-        Constraint state = instructionAllowedForInlining(method, instruction);
-        if (state == Constraint.NEVER) {
-          return Constraint.NEVER;
-        }
-        if (state.ordinal() < result.ordinal()) {
-          result = state;
-        }
+    InstructionIterator it = code.instructionIterator();
+    while (it.hasNext()) {
+      Instruction instruction = it.next();
+      Constraint state = instructionAllowedForInlining(method, instruction);
+      if (state == Constraint.NEVER) {
+        return Constraint.NEVER;
       }
-      block = nextBlock;
-    } while (block != null);
+      if (state.ordinal() < result.ordinal()) {
+        result = state;
+      }
+    }
     return result;
   }
 
   boolean hasInliningAccess(DexEncodedMethod method, DexEncodedMethod target) {
-    if (target.accessFlags.isPublic()) {
+    if (!isVisibleWithFlags(target.method.holder, method.method.holder, target.accessFlags)) {
+      return false;
+    }
+    // The class needs also to be visible for us to have access.
+    DexClass targetClass = appInfo.definitionFor(target.method.holder);
+    return isVisibleWithFlags(target.method.holder, method.method.holder, targetClass.accessFlags);
+  }
+
+  private boolean isVisibleWithFlags(DexType target, DexType context, DexAccessFlags flags) {
+    if (flags.isPublic()) {
       return true;
     }
-    DexType methodHolder = method.method.getHolder();
-    DexType targetHolder = target.method.getHolder();
-    if (target.accessFlags.isPrivate()) {
-      return methodHolder == targetHolder;
+    if (flags.isPrivate()) {
+      return target == context;
     }
-    if (target.accessFlags.isProtected() &&
-        methodHolder.isSubtypeOf(targetHolder, appInfo)) {
-      return true;
+    if (flags.isProtected()) {
+      return context.isSubtypeOf(target, appInfo) || target.isSamePackage(context);
     }
-    return methodHolder.isSamePackage(targetHolder);
+    // package-private
+    return target.isSamePackage(context);
   }
 
   synchronized DexEncodedMethod doubleInlining(DexEncodedMethod method,
@@ -141,14 +136,59 @@
     }
   }
 
+  /**
+   * Encodes the constraints for inlining a method's instructions into a different context.
+   * <p>
+   * This only takes the instructions into account and not whether a method should be inlined
+   * or what reason for inlining it might have. Also, it does not take the visibility of the
+   * method itself into account.
+   */
   public enum Constraint {
     // The ordinal values are important so please do not reorder.
-    NEVER,    // Never inline this.
-    PRIVATE,  // Only inline this into methods with same holder.
-    PACKAGE,  // Only inline this into methods with holders from same package.
-    ALWAYS,   // No restrictions for inlining this.
+    NEVER,     // Never inline this.
+    SAMECLASS, // Only inline this into methods with same holder.
+    PACKAGE,   // Only inline this into methods with holders from same package.
+    SUBCLASS,  // Only inline this into methods with holders from a subclass.
+    ALWAYS;    // No restrictions for inlining this.
+
+    static {
+      assert NEVER.ordinal() < SAMECLASS.ordinal();
+      assert SAMECLASS.ordinal() < PACKAGE.ordinal();
+      assert PACKAGE.ordinal() < SUBCLASS.ordinal();
+      assert SUBCLASS.ordinal() < ALWAYS.ordinal();
+    }
+
+    public static Constraint deriveConstraint(DexType contextHolder, DexType targetHolder,
+        DexAccessFlags flags, AppInfoWithSubtyping appInfo) {
+      if (flags.isPublic()) {
+        return ALWAYS;
+      } else if (flags.isPrivate()) {
+        return targetHolder == contextHolder ? SAMECLASS : NEVER;
+      } else if (flags.isProtected()) {
+        if (targetHolder.isSamePackage(contextHolder)) {
+          // Even though protected, this is visible via the same package from the context.
+          return PACKAGE;
+        } else if (contextHolder.isSubtypeOf(targetHolder, appInfo)) {
+          return SUBCLASS;
+        }
+        return NEVER;
+      } else {
+      /* package-private */
+        return targetHolder.isSamePackage(contextHolder) ? PACKAGE : NEVER;
+      }
+    }
+
+    public static Constraint min(Constraint one, Constraint other) {
+      return one.ordinal() < other.ordinal() ? one : other;
+    }
   }
 
+  /**
+   * Encodes the reason why a method should be inlined.
+   * <p>
+   * This is independent of determining whether a method can be inlined, except for the FORCE
+   * state, that will inline a method irrespective of visibility and instruction checks.
+   */
   public enum Reason {
     FORCE,         // Inlinee is marked for forced inlining (bridge method or renamed constructor).
     SINGLE_CALLER, // Inlinee has precisely one caller.
@@ -199,7 +239,8 @@
     return numOfInstructions;
   }
 
-  private boolean legalConstructorInline(DexEncodedMethod method, IRCode code) {
+  private boolean legalConstructorInline(DexEncodedMethod method,
+      InvokeMethod invoke, IRCode code) {
     // In the Java VM Specification section "4.10.2.4. Instance Initialization Methods and
     // Newly Created Objects" it says:
     //
@@ -208,29 +249,43 @@
     // declared within myClass.
     //
 
-    // Allow inlining a constructor into a constructor, as the constructor code is expected to
-    // adhere to the VM specification.
-    if (method.accessFlags.isConstructor()) {
+    // Allow inlining a constructor into a constructor of the same class, as the constructor code
+    // is expected to adhere to the VM specification.
+    if (method.accessFlags.isConstructor()
+        && method.method.holder == invoke.getInvokedMethod().holder) {
       return true;
     }
 
     // Don't allow inlining a constructor into a non-constructor if the first use of the
     // un-initialized object is not an argument of an invoke of <init>.
+    // Also, we cannot inline a constructor if it initializes final fields, as such is only allowed
+    // from within a constructor of the corresponding class.
     InstructionIterator iterator = code.instructionIterator();
     Instruction instruction = iterator.next();
     // A constructor always has the un-initialized object as the first argument.
     assert instruction.isArgument();
     Value unInitializedObject = instruction.outValue();
+    boolean seenSuperInvoke = false;
     while (iterator.hasNext()) {
       instruction = iterator.next();
       if (instruction.inValues().contains(unInitializedObject)) {
-        return instruction.isInvokeDirect()
-            && appInfo.dexItemFactory
-            .isConstructor(instruction.asInvokeDirect().getInvokedMethod());
+        if (instruction.isInvokeDirect() && !seenSuperInvoke) {
+          DexMethod target = instruction.asInvokeDirect().getInvokedMethod();
+          seenSuperInvoke = appInfo.dexItemFactory.isConstructor(target);
+        }
+        if (!seenSuperInvoke) {
+          return false;
+        }
+      }
+      if (instruction.isInstancePut()) {
+        DexField field = instruction.asInstancePut().getField();
+        DexEncodedField target = appInfo.lookupInstanceTarget(field.getHolder(), field);
+        if (target != null && target.accessFlags.isFinal()) {
+          return false;
+        }
       }
     }
-    assert false : "Execution should never reach this point";
-    return false;
+    return true;
   }
 
   /// Computer the receiver value for the holder method.
@@ -250,8 +305,7 @@
       return;
     }
     computeReceiverMustBeNonNull(code);
-    Value receiver = receiverValue(method, code);
-    InliningOracle oracle = new InliningOracle(this, method, receiver, callGraph);
+    InliningOracle oracle = new InliningOracle(this, method, callGraph);
 
     List<BasicBlock> blocksToRemove = new ArrayList<>();
     ListIterator<BasicBlock> blockIterator = code.listIterator();
@@ -272,8 +326,7 @@
               // The declared target cannot be found so skip inlining.
               continue;
             }
-            boolean forceInline = result.reason == Reason.FORCE;
-            if (!target.isProcessed() && !forceInline) {
+            if (!(target.isProcessed() || result.reason == Reason.FORCE)) {
               // Do not inline code that was not processed unless we have to force inline.
               continue;
             }
@@ -291,20 +344,16 @@
               // If this code did not go through the full pipeline, apply inlining to make sure
               // that force inline targets get processed.
               if (!target.isProcessed()) {
-                assert forceInline;
+                assert result.reason == Reason.FORCE;
                 if (Log.ENABLED) {
                   Log.verbose(getClass(), "Forcing extra inline on " + target.toSourceString());
                 }
                 performInlining(target, inlinee, callGraph);
               }
               // Make sure constructor inlining is legal.
-              if (target.accessFlags.isConstructor() && !legalConstructorInline(method, inlinee)) {
-                continue;
-              }
-              // Ensure the container is compatible with the target.
-              if (!forceInline
-                  && !result.target.isPublicInlining()
-                  && (method.method.getHolder() != result.target.method.getHolder())) {
+              if (target.accessFlags.isConstructor()
+                  && !target.accessFlags.isStatic()
+                  && !legalConstructorInline(method, invoke, inlinee)) {
                 continue;
               }
               DexType downcast = null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
index 446b75a..fb6c2eb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -3,18 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
+import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeInterface;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.ir.code.InvokePolymorphic;
 import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.InvokeSuper;
-import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.CallGraph;
 import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -26,20 +22,19 @@
  */
 public class InliningOracle {
 
-  final Inliner inliner;
-  final DexEncodedMethod method;
-  final Value receiver;
-  final CallGraph callGraph;
-  final private InliningInfo info;
+  private static final int INLINING_INSTRUCTION_LIMIT = 5;
+
+  private final Inliner inliner;
+  private final DexEncodedMethod method;
+  private final CallGraph callGraph;
+  private final InliningInfo info;
 
   public InliningOracle(
       Inliner inliner,
       DexEncodedMethod method,
-      Value receiver,
       CallGraph callGraph) {
     this.inliner = inliner;
     this.method = method;
-    this.receiver = receiver;
     this.callGraph = callGraph;
     info = Log.ENABLED ? new InliningInfo(method) : null;
   }
@@ -50,7 +45,7 @@
     }
   }
 
-  DexEncodedMethod validateCandidate(InvokeMethod invoke) {
+  private DexEncodedMethod validateCandidate(InvokeMethod invoke) {
     DexEncodedMethod candidate = invoke.computeSingleTarget(inliner.appInfo);
     if ((candidate == null)
         || (candidate.getCode() == null)
@@ -60,32 +55,6 @@
       }
       return null;
     }
-    if (method == candidate) {
-      // Cannot handle recursive inlining at this point.
-      // Bridge methods should never have recursive calls.
-      assert !candidate.getOptimizationInfo().forceInline();
-      return null;
-    }
-
-    if (candidate.accessFlags.isSynchronized()) {
-      // Don't inline if target is synchronized.
-      if (info != null) {
-        info.exclude(invoke, "Inlinee candidate is synchronized");
-      }
-      return null;
-    }
-
-    if (callGraph.isBreaker(method, candidate)) {
-      // Cycle breaker so abort to preserve compilation order.
-      return null;
-    }
-
-    if (!inliner.hasInliningAccess(method, candidate)) {
-      if (info != null) {
-        info.exclude(invoke, "Inlinee candidate does not have right access flags");
-      }
-      return null;
-    }
     return candidate;
   }
 
@@ -102,104 +71,6 @@
     return Reason.SIMPLE;
   }
 
-  public InlineAction computeForInvokeWithReceiver(InvokeMethodWithReceiver invoke) {
-    boolean receiverIsNeverNull = invoke.receiverIsNeverNull();
-    if (!receiverIsNeverNull) {
-      if (info != null) {
-        info.exclude(invoke, "receiver for candidate can be null");
-      }
-      return null;
-    }
-    DexEncodedMethod target = invoke.computeSingleTarget(inliner.appInfo);
-    if (target == null) {
-      // Abort inlining attempt if we cannot find single target.
-      if (info != null) {
-        info.exclude(invoke, "could not find single target");
-      }
-      return null;
-    }
-
-    if (target == method) {
-      // Bridge methods should never have recursive calls.
-      assert !target.getOptimizationInfo().forceInline();
-      return null;
-    }
-
-    if (target.getCode() == null) {
-      return null;
-    }
-
-    DexClass holder = inliner.appInfo.definitionFor(target.method.getHolder());
-    if (holder.isInterface()) {
-      // Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception at runtime.
-      if (info != null) {
-        info.exclude(invoke, "Do not inline target if method holder is an interface class");
-      }
-      return null;
-    }
-
-    if (holder.isLibraryClass()) {
-      // Library functions should not be inlined.
-      return null;
-    }
-
-    // Don't inline if target is synchronized.
-    if (target.accessFlags.isSynchronized()) {
-      if (info != null) {
-        info.exclude(invoke, "target is synchronized");
-      }
-      return null;
-    }
-
-    Reason reason = computeInliningReason(target);
-    // Determine if this should be inlined no matter how big it is.
-    if (!target.isInliningCandidate(method, reason != Reason.SIMPLE)) {
-      // Abort inlining attempt if the single target is not an inlining candidate.
-      if (info != null) {
-        info.exclude(invoke, "target is not identified for inlining");
-      }
-      return null;
-    }
-
-    if (callGraph.isBreaker(method, target)) {
-      // Cycle breaker so abort to preserve compilation order.
-      return null;
-    }
-
-    // Abort inlining attempt if method -> target access is not right.
-    if (!inliner.hasInliningAccess(method, target)) {
-      if (info != null) {
-        info.exclude(invoke, "target does not have right access");
-      }
-      return null;
-    }
-
-    // Attempt to inline a candidate that is only called twice.
-    if ((reason == Reason.DUAL_CALLER) && (inliner.doubleInlining(method, target) == null)) {
-      if (info != null) {
-        info.exclude(invoke, "target is not ready for double inlining");
-      }
-      return null;
-    }
-
-    if (info != null) {
-      info.include(invoke.getType(), target);
-    }
-    return new InlineAction(target, invoke, reason);
-  }
-
-  public InlineAction computeForInvokeVirtual(InvokeVirtual invoke) {
-    return computeForInvokeWithReceiver(invoke);
-  }
-
-  public InlineAction computeForInvokeInterface(InvokeInterface invoke) {
-    return computeForInvokeWithReceiver(invoke);
-  }
-
-  public InlineAction computeForInvokeDirect(InvokeDirect invoke) {
-    return computeForInvokeWithReceiver(invoke);
-  }
-
   private boolean canInlineStaticInvoke(DexEncodedMethod method, DexEncodedMethod target) {
     // Only proceed with inlining a static invoke if:
     // - the holder for the target equals the holder for the method, or
@@ -209,7 +80,9 @@
       return true;
     }
     DexClass clazz = inliner.appInfo.definitionFor(targetHolder);
-    return (clazz != null) && (!clazz.hasNonTrivialClassInitializer());
+    return (clazz != null)
+        && (!clazz.hasNonTrivialClassInitializer())
+        && (!clazz.defaultValuesForStaticFieldsMayTriggerAllocation());
   }
 
   private synchronized boolean isDoubleInliningTarget(DexEncodedMethod candidate) {
@@ -219,14 +92,113 @@
         && (candidate.getCode().asDexCode().instructions.length <= 10);
   }
 
+  private boolean passesInliningConstraints(InvokeMethod invoke, DexEncodedMethod candidate,
+      Reason reason) {
+    if (callGraph.isBreaker(method, candidate)) {
+      // Cycle breaker so abort to preserve compilation order.
+      return false;
+    }
+
+    if (method == candidate) {
+      // Cannot handle recursive inlining at this point.
+      // Force inlined method should never be recursive.
+      assert !candidate.getOptimizationInfo().forceInline();
+      if (info != null) {
+        info.exclude(invoke, "direct recursion");
+      }
+      return false;
+    }
+
+    // Abort inlining attempt if method -> target access is not right.
+    if (!inliner.hasInliningAccess(method, candidate)) {
+      if (info != null) {
+        info.exclude(invoke, "target does not have right access");
+      }
+      return false;
+    }
+
+    DexClass holder = inliner.appInfo.definitionFor(candidate.method.getHolder());
+    if (holder.isInterface()) {
+      // Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception at
+      // runtime.
+      if (info != null) {
+        info.exclude(invoke, "Do not inline target if method holder is an interface class");
+      }
+      return false;
+    }
+
+    if (holder.isLibraryClass()) {
+      // Library functions should not be inlined.
+      return false;
+    }
+
+    // Don't inline if target is synchronized.
+    if (candidate.accessFlags.isSynchronized()) {
+      if (info != null) {
+        info.exclude(invoke, "target is synchronized");
+      }
+      return false;
+    }
+
+    // Attempt to inline a candidate that is only called twice.
+    if ((reason == Reason.DUAL_CALLER) && (inliner.doubleInlining(method, candidate) == null)) {
+      if (info != null) {
+        info.exclude(invoke, "target is not ready for double inlining");
+      }
+      return false;
+    }
+
+    if (reason == Reason.SIMPLE) {
+      // If we are looking for a simple method, only inline if actually simple.
+      Code code = candidate.getCode();
+      if (code.estimatedSizeForInlining() > INLINING_INSTRUCTION_LIMIT) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public InlineAction computeForInvokeWithReceiver(InvokeMethodWithReceiver invoke) {
+    boolean receiverIsNeverNull = invoke.receiverIsNeverNull();
+    if (!receiverIsNeverNull) {
+      if (info != null) {
+        info.exclude(invoke, "receiver for candidate can be null");
+      }
+      return null;
+    }
+    DexEncodedMethod candidate = validateCandidate(invoke);
+    if (candidate == null) {
+      return null;
+    }
+
+    Reason reason = computeInliningReason(candidate);
+    if (!candidate.isInliningCandidate(method, reason == Reason.FORCE, inliner.appInfo)) {
+      // Abort inlining attempt if the single target is not an inlining candidate.
+      if (info != null) {
+        info.exclude(invoke, "target is not identified for inlining");
+      }
+      return null;
+    }
+
+    if (!passesInliningConstraints(invoke, candidate, reason)) {
+      return null;
+    }
+
+    if (info != null) {
+      info.include(invoke.getType(), candidate);
+    }
+    return new InlineAction(candidate, invoke, reason);
+  }
+
   public InlineAction computeForInvokeStatic(InvokeStatic invoke) {
     DexEncodedMethod candidate = validateCandidate(invoke);
     if (candidate == null) {
       return null;
     }
+
     Reason reason = computeInliningReason(candidate);
     // Determine if this should be inlined no matter how big it is.
-    if (!candidate.isInliningCandidate(method, reason != Reason.SIMPLE)) {
+    if (!candidate.isInliningCandidate(method, reason == Reason.FORCE, inliner.appInfo)) {
       // Abort inlining attempt if the single target is not an inlining candidate.
       if (info != null) {
         info.exclude(invoke, "target is not identified for inlining");
@@ -242,11 +214,7 @@
       return null;
     }
 
-    // Attempt to inline a candidate that is only called twice.
-    if ((reason == Reason.DUAL_CALLER) && (inliner.doubleInlining(method, candidate) == null)) {
-      if (info != null) {
-        info.exclude(invoke, "target is not ready for double inlining");
-      }
+    if (!passesInliningConstraints(invoke, candidate, reason)) {
       return null;
     }
 
@@ -256,20 +224,6 @@
     return new InlineAction(candidate, invoke, reason);
   }
 
-  public InlineAction computeForInvokeSuper(InvokeSuper invoke) {
-    DexEncodedMethod candidate = validateCandidate(invoke);
-    if (candidate == null) {
-      if (info != null) {
-        info.exclude(invoke, "not a valid inlining target");
-      }
-      return null;
-    }
-    if (info != null) {
-      info.include(invoke.getType(), candidate);
-    }
-    return new InlineAction(candidate, invoke, Reason.SIMPLE);
-  }
-
   public InlineAction computeForInvokePolymorpic(InvokePolymorphic invoke) {
     // TODO: No inlining of invoke polymorphic for now.
     if (info != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index c2e804e..8cc8ec4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -958,6 +958,12 @@
     }
 
     @Override
+    public int estimatedSizeForInlining() {
+      // We just onlined this, so do not inline it again.
+      return Integer.MAX_VALUE;
+    }
+
+    @Override
     public OutlineCode asOutlineCode() {
       return this;
     }
diff --git a/src/test/examples/inlining/InlineConstructorFinalField.java b/src/test/examples/inlining/InlineConstructorFinalField.java
new file mode 100644
index 0000000..093a2ac
--- /dev/null
+++ b/src/test/examples/inlining/InlineConstructorFinalField.java
@@ -0,0 +1,23 @@
+// 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 inlining;
+
+public class InlineConstructorFinalField {
+
+  public final int number;
+
+  @CheckDiscarded
+  InlineConstructorFinalField(int value) {
+    number = value;
+  }
+
+  // This will not be inlined, as it sets a final field.
+  InlineConstructorFinalField() {
+    this(42);
+  }
+
+  public String toString() {
+    return "value: " + number;
+  }
+}
diff --git a/src/test/examples/inlining/Inlining.java b/src/test/examples/inlining/Inlining.java
index e013655..bd8870f 100644
--- a/src/test/examples/inlining/Inlining.java
+++ b/src/test/examples/inlining/Inlining.java
@@ -3,6 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package inlining;
 
+import inlining.pkg.OtherPublicClass;
+import inlining.pkg.PublicClass;
+import inlining.pkg.Subclass;
+
 class A {
 
   int a;
@@ -14,6 +18,14 @@
   int a() {
     return a;
   }
+
+  int cannotInline(int v) {
+    // Cannot inline due to recursion.
+    if (v > 0) {
+      return cannotInline(v - 1);
+    }
+    return 42;
+  }
 }
 
 class B extends A {
@@ -21,6 +33,14 @@
   B(int a) {
     super(a);
   }
+
+  int cannotInline(int v) {
+    return -1;
+  }
+
+  int callMethodInSuper() {
+    return super.cannotInline(10);
+  }
 }
 
 class InlineConstructor {
@@ -134,6 +154,21 @@
     Assert(ic != null);
     InlineConstructorOfInner icoi = new InlineConstructorOfInner();
     Assert(icoi != null);
+
+    // Check that super calls are processed correctly.
+    new B(123).callMethodInSuper();
+
+    // Inline calls to package private methods
+    PublicClass.alsoCallsPackagePrivateMethod();
+    OtherPublicClass.callsMethodThatCallsPackagePrivateMethod();
+    // Inline calls to protected methods.
+    PublicClass.callsProtectedMethod3();
+    PublicClass.alsoReadsPackagePrivateField();
+    OtherPublicClass.callsMethodThatCallsProtectedMethod();
+    OtherPublicClass.callsMethodThatReadsFieldInPackagePrivateClass();
+    Subclass.callsMethodThatCallsProtectedMethod();
+    // Do not inline constructors which set final field.
+    System.out.println(new InlineConstructorFinalField());
   }
 
   private static boolean intCmpExpression(A a, A b) {
@@ -180,6 +215,7 @@
     return 21.21F == floatConstantInline();
   }
 
+  @CheckDiscarded
   private static String stringConstantInline() {
     return "Fisk er godt";
   }
diff --git a/src/test/examples/inlining/keep-rules-discard.txt b/src/test/examples/inlining/keep-rules-discard.txt
index 66be962..ea5e143 100644
--- a/src/test/examples/inlining/keep-rules-discard.txt
+++ b/src/test/examples/inlining/keep-rules-discard.txt
@@ -8,9 +8,6 @@
   public static void main(...);
 }
 
-# allow access modification to enable minifcation
--allowaccessmodification
-
 # check that methods have been inlined
 -checkdiscard class * {
   @inlining.CheckDiscarded *;
diff --git a/src/test/examples/inlining/keep-rules.txt b/src/test/examples/inlining/keep-rules.txt
index dd713bb..d60f1dc 100644
--- a/src/test/examples/inlining/keep-rules.txt
+++ b/src/test/examples/inlining/keep-rules.txt
@@ -7,6 +7,3 @@
 -keep public class inlining.Inlining {
   public static void main(...);
 }
-
-# allow access modification to enable minifcation
--allowaccessmodification
diff --git a/src/test/examples/inlining/pkg/OtherPublicClass.java b/src/test/examples/inlining/pkg/OtherPublicClass.java
new file mode 100644
index 0000000..4d781d9
--- /dev/null
+++ b/src/test/examples/inlining/pkg/OtherPublicClass.java
@@ -0,0 +1,20 @@
+// 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 inlining.pkg;
+
+public class OtherPublicClass {
+
+  public static String callsMethodThatCallsPackagePrivateMethod() {
+    return PublicClass.callsPackagePrivateMethod();
+  }
+
+  public static String callsMethodThatCallsProtectedMethod() {
+    return PublicClass.callsProtectedMethod();
+  }
+
+  public static int callsMethodThatReadsFieldInPackagePrivateClass() {
+    return PublicClass.readsPackagePrivateField();
+  }
+
+}
diff --git a/src/test/examples/inlining/pkg/PackagePrivateClass.java b/src/test/examples/inlining/pkg/PackagePrivateClass.java
new file mode 100644
index 0000000..ae05e8b
--- /dev/null
+++ b/src/test/examples/inlining/pkg/PackagePrivateClass.java
@@ -0,0 +1,9 @@
+// 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 inlining.pkg;
+
+class PackagePrivateClass {
+
+  public static int aField = 42;
+}
diff --git a/src/test/examples/inlining/pkg/PublicClass.java b/src/test/examples/inlining/pkg/PublicClass.java
new file mode 100644
index 0000000..17cdea4
--- /dev/null
+++ b/src/test/examples/inlining/pkg/PublicClass.java
@@ -0,0 +1,59 @@
+// 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 inlining.pkg;
+
+import inlining.CheckDiscarded;
+
+public class PublicClass {
+
+  protected static String protectedMethod() {
+    return "Hello";
+  }
+
+  @CheckDiscarded
+  static String callsProtectedMethod() {
+    return protectedMethod();
+  }
+
+  @CheckDiscarded
+  static String callsProtectedMethod2() {
+    return protectedMethod();
+  }
+
+  public static String callsProtectedMethod3() {
+    return protectedMethod();
+  }
+
+  static String packagePrivateMethod() {
+    return "World";
+  }
+
+  @CheckDiscarded
+  static int readsPackagePrivateField() {
+    return PackagePrivateClass.aField;
+  }
+
+  public static int alsoReadsPackagePrivateField() {
+    return PackagePrivateClass.aField;
+  }
+
+  @CheckDiscarded
+  public static String callsPackagePrivateMethod() {
+    return packagePrivateMethod();
+  }
+
+  public static String alsoCallsPackagePrivateMethod() {
+    return packagePrivateMethod();
+  }
+
+  public static void callMeToPreventInling() {
+    // Call it three times so it does not get inlined.
+    packagePrivateMethod();
+    packagePrivateMethod();
+    packagePrivateMethod();
+    protectedMethod();
+    protectedMethod();
+    protectedMethod();
+  }
+}
diff --git a/src/test/examples/inlining/pkg/Subclass.java b/src/test/examples/inlining/pkg/Subclass.java
new file mode 100644
index 0000000..f917697
--- /dev/null
+++ b/src/test/examples/inlining/pkg/Subclass.java
@@ -0,0 +1,11 @@
+// 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 inlining.pkg;
+
+public class Subclass extends PublicClass {
+
+  public static String callsMethodThatCallsProtectedMethod() {
+    return PublicClass.callsProtectedMethod2();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 22278e2..b83b193 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -743,6 +743,18 @@
                   DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1, DexVm.ART_7_0_0)))
           .build();
 
+  public static List<String> requireInliningToBeDisabled = ImmutableList.of(
+      // Test for a specific stack trace that gets destroyed by inlining.
+      "492-checker-inline-invoke-interface",
+      "493-checker-inline-invoke-interface",
+      "488-checker-inline-recursive-calls",
+      "487-checker-inline-calls",
+      "122-npe",
+
+      // Calls some internal art methods that cannot tolerate inlining.
+      "466-get-live-vreg"
+  );
+
   private static List<String> failuresToTriage = ImmutableList.of(
       // This is flaky.
       "104-growth-limit",
@@ -812,11 +824,14 @@
     private final boolean failsWithArtOriginalOnly;
     // Test might produce different outputs.
     private final boolean outputMayDiffer;
+    // Whether to disable inlining
+    private final boolean disableInlining;
 
     TestSpecification(String name, DexTool dexTool,
         File directory, boolean skipArt, boolean skipTest, boolean failsWithX8,
         boolean failsWithArt, boolean failsWithArtOutput, boolean failsWithArtOriginalOnly,
-        String nativeLibrary, boolean expectedToFailWithX8, boolean outputMayDiffer) {
+        String nativeLibrary, boolean expectedToFailWithX8, boolean outputMayDiffer,
+        boolean disableInlining) {
       this.name = name;
       this.dexTool = dexTool;
       this.nativeLibrary = nativeLibrary;
@@ -829,12 +844,13 @@
       this.failsWithArtOriginalOnly = failsWithArtOriginalOnly;
       this.expectedToFailWithX8 = expectedToFailWithX8;
       this.outputMayDiffer = outputMayDiffer;
+      this.disableInlining = disableInlining;
     }
 
     TestSpecification(String name, DexTool dexTool, File directory, boolean skipArt,
         boolean failsWithArt) {
       this(name, dexTool, directory, skipArt,
-          false, false, failsWithArt, false, false, null, false, false);
+          false, false, failsWithArt, false, false, null, false, false, false);
     }
 
     public File resolveFile(String name) {
@@ -980,7 +996,8 @@
                 failsRunWithArtOriginalOnly.contains(name),
                 useNativeLibrary.contains(name) ? "arttest" : null,
                 expectedToFailWithCompilerSet.contains(name),
-                outputMayDiffer.contains(name)));
+                outputMayDiffer.contains(name),
+                requireInliningToBeDisabled.contains(name)));
       }
     }
     return data;
@@ -1049,9 +1066,11 @@
       CompilerUnderTest compilerUnderTest,
       Collection<String> fileNames,
       String resultPath,
-      CompilationMode compilationMode)
+      CompilationMode compilationMode,
+      boolean disableInlining)
       throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
-    executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null);
+    executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null,
+        disableInlining);
   }
 
   private void executeCompilerUnderTest(
@@ -1059,7 +1078,8 @@
       Collection<String> fileNames,
       String resultPath,
       CompilationMode mode,
-      String keepRulesFile)
+      String keepRulesFile,
+      boolean disableInlining)
       throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
     assert mode != null;
     switch (compilerUnderTest) {
@@ -1100,6 +1120,9 @@
                 if (enableInterfaceMethodDesugaring.contains(name)) {
                   options.interfaceMethodDesugaring = OffOrAuto.Auto;
                 }
+                if (disableInlining) {
+                  options.inlineAccessors = false;
+                }
               });
           break;
         }
@@ -1292,7 +1315,8 @@
       DexVm dexVm,
       File resultDir)
       throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
-    executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getAbsolutePath(), mode);
+    executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getAbsolutePath(), mode,
+        specification.disableInlining);
 
     if (!ToolHelper.artSupported()) {
       return;
@@ -1445,7 +1469,8 @@
       thrown.expect(CompilationError.class);
       try {
         executeCompilerUnderTest(
-            compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode, null);
+            compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
+            specification.disableInlining);
       } catch (CompilationException e) {
         throw new CompilationError(e.getMessage(), e);
       } catch (ExecutionException e) {
@@ -1456,12 +1481,14 @@
     } else if (specification.failsWithX8) {
       thrown.expect(Throwable.class);
       executeCompilerUnderTest(
-          compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode);
+          compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
+          specification.disableInlining);
       System.err.println("Should have failed R8/D8 compilation with an exception.");
       return;
     } else {
       executeCompilerUnderTest(
-          compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode);
+          compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
+          specification.disableInlining);
     }
 
     if (!specification.skipArt && ToolHelper.artSupported()) {
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index 47c6330..c9b476a 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -151,10 +151,13 @@
   }
 
   private static void inspectShaking9(PrintUsageInspector inspector) {
-    assertFalse(inspector.clazz("shaking9.Superclass").isPresent());
+    Optional<ClassSubject> superClass = inspector.clazz("shaking9.Superclass");
+    assertTrue(superClass.isPresent());
+    assertTrue(superClass.get().method("void", "<init>", Collections.emptyList()));
     Optional<ClassSubject> subClass = inspector.clazz("shaking9.Subclass");
     assertTrue(subClass.isPresent());
     assertTrue(subClass.get().method("void", "aMethod", Collections.emptyList()));
+    assertTrue(subClass.get().method("void", "<init>", Collections.emptyList()));
   }
 
   private static void inspectShaking12(PrintUsageInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 5b94e6b..3435577 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -73,8 +73,12 @@
       "minifygeneric:keep-rules.txt:DEX:false",
       "minifygeneric:keep-rules.txt:JAR:false",
       "minifygenericwithinner:keep-rules.txt:DEX:false",
-      "minifygenericwithinner:keep-rules.txt:JAR:false"
-
+      "minifygenericwithinner:keep-rules.txt:JAR:false",
+      // TODO(62048823): Inlining tests don't use allowaccessmodification.
+      "inlining:keep-rules.txt:DEX:true",
+      "inlining:keep-rules.txt:JAR:true",
+      "inlining:keep-rules-discard.txt:DEX:true",
+      "inlining:keep-rules-discard.txt:JAR:true"
   );
   private final boolean minify;
 
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 4808f33..4656cd6 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -43,6 +43,13 @@
 
 public class OutlineTest extends SmaliTestBase {
 
+  private InternalOptions createInternalOptions() {
+    InternalOptions result = new InternalOptions();
+    // Disable inlining to make sure that code looks as expected.
+    result.inlineAccessors = false;
+    return result;
+  }
+
   DexEncodedMethod getInvokedMethod(DexApplication application, InvokeStatic invoke) {
     DexInspector inspector = new DexInspector(application);
     ClassSubject clazz = inspector.clazz(invoke.getMethod().holder.toSourceString());
@@ -118,7 +125,7 @@
     );
 
     for (int i = 2; i < 6; i++) {
-      InternalOptions options = new InternalOptions();
+      InternalOptions options = createInternalOptions();
       options.outline.threshold = 1;
       options.outline.minSize = i;
       options.outline.maxSize = i;
@@ -183,7 +190,7 @@
     );
 
     for (int i = 2; i < 6; i++) {
-      InternalOptions options = new InternalOptions();
+      InternalOptions options = createInternalOptions();
       options.outline.threshold = 1;
       options.outline.minSize = i;
       options.outline.maxSize = i;
@@ -249,7 +256,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     DexApplication originalApplication = buildApplication(builder, options);
     DexApplication processedApplication = processApplication(originalApplication, options);
@@ -313,7 +320,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     DexApplication originalApplication = buildApplication(builder, options);
     DexApplication processedApplication = processApplication(originalApplication, options);
@@ -372,7 +379,7 @@
     );
 
     for (int i = 2; i < 4; i++) {
-      InternalOptions options = new InternalOptions();
+      InternalOptions options = createInternalOptions();
       options.outline.threshold = 1;
       options.outline.minSize = i;
       options.outline.maxSize = i;
@@ -446,7 +453,7 @@
     );
 
     for (int i = 2; i < 4; i++) {
-      InternalOptions options = new InternalOptions();
+      InternalOptions options = createInternalOptions();
       options.outline.threshold = 1;
       options.outline.minSize = i;
       options.outline.maxSize = i;
@@ -516,7 +523,7 @@
     );
 
     for (int i = 2; i < 6; i++) {
-      InternalOptions options = new InternalOptions();
+      InternalOptions options = createInternalOptions();
       options.outline.threshold = 1;
       options.outline.minSize = i;
       options.outline.maxSize = i;
@@ -614,7 +621,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 7;
     options.outline.maxSize = 7;
@@ -678,7 +685,7 @@
     );
 
     for (int i = 2; i < 8; i++) {
-      InternalOptions options = new InternalOptions();
+      InternalOptions options = createInternalOptions();
       options.outline.threshold = 1;
       options.outline.minSize = i;
       options.outline.maxSize = i;
@@ -735,7 +742,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 3;
     options.outline.maxSize = 3;
@@ -808,7 +815,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 3;
     options.outline.maxSize = 3;
@@ -879,7 +886,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 3;
     options.outline.maxSize = 3;
@@ -943,7 +950,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 5;
     options.outline.maxSize = 5;
@@ -1003,7 +1010,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 4;
     options.outline.maxSize = 4;
@@ -1077,7 +1084,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 4;
     options.outline.maxSize = 4;
@@ -1152,7 +1159,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 3;  // Outline add, sub and mul.
     options.outline.maxSize = 3;
@@ -1204,7 +1211,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 3;
     options.outline.maxSize = 3;
@@ -1249,7 +1256,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 1;
     options.outline.minSize = 3;
     options.outline.maxSize = 3;
@@ -1455,7 +1462,7 @@
         "    return-void"
     );
 
-    InternalOptions options = new InternalOptions();
+    InternalOptions options = createInternalOptions();
     options.outline.threshold = 2;
 
     DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
diff --git a/src/test/java/com/android/tools/r8/utils/R8InliningTest.java b/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
index 50db347..7f3c3e1 100644
--- a/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
@@ -85,7 +85,10 @@
             .setOutputPath(out)
             .addProguardConfigurationFiles(Paths.get(keepRulesFile))
             .build();
-    ToolHelper.runR8(command);
+    // TODO(62048823): Enable minification.
+    ToolHelper.runR8(command, o -> {
+      o.skipMinification = true;
+    });
     ToolHelper.runArtNoVerificationErrors(out + "/classes.dex", "inlining.Inlining");
   }