Move querying for reachability sensitivity to DexProgramClass

This avoids maintaining the same reachability sensitivity bit on each method's optimization info, and also allows for querying reachability sensitivity at any point, unlike previously, where the information was only available after having been published to the method optimization info.

Change-Id: I4650d5496179ba9c4e7237e6130e1db004cf2a52
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index 28d7592..351a3a1 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -175,8 +175,7 @@
   private ImmutableList<BasicBlock> computeLivenessInformation() {
     ImmutableList<BasicBlock> blocks = code.numberInstructions();
     liveAtEntrySets = code.computeLiveAtEntrySets();
-    LinearScanRegisterAllocator.computeLiveRanges(
-        appView.options(), code, liveAtEntrySets, liveIntervals);
+    LinearScanRegisterAllocator.computeLiveRanges(appView, code, liveAtEntrySets, liveIntervals);
     return blocks;
   }
 
diff --git a/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java b/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
index 7a33b3c..173ddd1 100644
--- a/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
+++ b/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.LebUtils;
@@ -79,18 +80,19 @@
     // (the sum of the normal debug info for all methods sharing the same max pc and param count.)
     Int2ReferenceMap<CostSummary> paramCountToCosts = new Int2ReferenceOpenHashMap<>();
     for (DexProgramClass clazz : file.classes()) {
-      IdentityHashMap<DexString, List<DexEncodedMethod>> overloads =
+      IdentityHashMap<DexString, List<ProgramMethod>> overloads =
           LineNumberOptimizer.groupMethodsByRenamedName(graphLens, namingLens, clazz);
-      for (List<DexEncodedMethod> methods : overloads.values()) {
+      for (List<ProgramMethod> methods : overloads.values()) {
         if (methods.size() != 1) {
           // Never use PC info for overloaded methods. They need distinct lines to disambiguate.
           continue;
         }
-        DexEncodedMethod method = methods.get(0);
-        if (!isPcCandidate(method)) {
+        ProgramMethod method = methods.get(0);
+        DexEncodedMethod definition = method.getDefinition();
+        if (!isPcCandidate(definition)) {
           continue;
         }
-        DexCode code = method.getCode().asDexCode();
+        DexCode code = definition.getCode().asDexCode();
         DexDebugInfo debugInfo = code.getDebugInfo();
         Instruction lastInstruction = getLastExecutableInstruction(code);
         if (lastInstruction == null) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 3bb7190..e7f4ad0 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -777,7 +777,7 @@
                     mapping,
                     application.dexItemFactory,
                     options.testing.forceJumboStringProcessing);
-            method.getDefinition().setCode(rewrittenCode.asCode(), appView);
+            method.setCode(rewrittenCode.asCode(), appView);
           });
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 3033fb7..5f31229 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -62,7 +62,6 @@
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.function.BiPredicate;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
@@ -445,8 +444,8 @@
     }
     if (parameterLabel != null) {
       assert localVariables.isEmpty();
-      Map<Integer, DebugLocalInfo> parameterInfo = method.getDefinition().getParameterInfo();
-      for (Entry<Integer, DebugLocalInfo> entry : parameterInfo.entrySet()) {
+      Int2ReferenceMap<DebugLocalInfo> parameterInfo = method.getDefinition().getParameterInfo();
+      for (Int2ReferenceMap.Entry<DebugLocalInfo> entry : parameterInfo.int2ReferenceEntrySet()) {
         writeLocalVariableEntry(
             visitor,
             graphLens,
@@ -454,7 +453,7 @@
             entry.getValue(),
             parameterLabel,
             parameterLabel,
-            entry.getKey());
+            entry.getIntKey());
       }
     } else {
       for (LocalVariableInfo local : localVariables) {
@@ -561,7 +560,7 @@
       Origin origin,
       RewrittenPrototypeDescription protoChanges,
       MutableMethodConversionOptions conversionOptions) {
-    if (!method.getDefinition().keepLocals(appView.options())) {
+    if (!method.keepLocals(appView)) {
       return internalBuild(
           Collections.emptyList(),
           context,
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
index 56a5988..07fd8c4 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -68,7 +68,7 @@
 
   public static boolean canonicalizeCodeIfPossible(AppView<?> appView, ProgramMethod method) {
     if (hasDefaultInstanceInitializerCode(method, appView)) {
-      method.getDefinition().setCode(get(), appView);
+      method.setCode(get(), appView);
       return true;
     }
     return false;
@@ -82,7 +82,7 @@
       AppView<?> appView, ProgramMethod method, DexType superType) {
     DexEncodedMethod definition = method.getDefinition();
     assert definition.getCode().isDefaultInstanceInitializerCode();
-    definition.setCode(get().toCfCode(method, appView.dexItemFactory(), superType), appView);
+    method.setCode(get().toCfCode(method, appView.dexItemFactory(), superType), appView);
   }
 
   private static boolean hasDefaultInstanceInitializerCode(
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 3871952..4196ca2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -77,7 +77,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -722,14 +721,10 @@
     compilationState = CompilationState.NOT_PROCESSED;
   }
 
-  public void setCode(Code newCode, AppView<?> appView) {
+  public void setCode(Code code, Int2ReferenceMap<DebugLocalInfo> parameterInfo) {
     checkIfObsolete();
-    // If the locals are not kept, we might still need information to satisfy -keepparameternames.
-    // The information needs to be retrieved on the original code object before replacing it.
-    if (code != null && code.isCfCode() && !hasParameterInfo() && !keepLocals(appView.options())) {
-      setParameterInfo(code.collectParameterInfo(this, appView));
-    }
-    code = newCode;
+    this.code = code;
+    this.parameterInfo = parameterInfo;
   }
 
   public void unsetCode() {
@@ -737,23 +732,11 @@
     code = null;
   }
 
-  public boolean keepLocals(InternalOptions options) {
-    if (options.testing.noLocalsTableOnInput) {
-      return false;
-    }
-    return options.debug || getOptimizationInfo().isReachabilitySensitive();
-  }
-
-  private void setParameterInfo(Int2ReferenceMap<DebugLocalInfo> parameterInfo) {
-    assert this.parameterInfo == NO_PARAMETER_INFO;
-    this.parameterInfo = parameterInfo;
-  }
-
   public boolean hasParameterInfo() {
     return parameterInfo != NO_PARAMETER_INFO;
   }
 
-  public Map<Integer, DebugLocalInfo> getParameterInfo() {
+  public Int2ReferenceMap<DebugLocalInfo> getParameterInfo() {
     return parameterInfo;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index f61a52b..354cc52 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.synthesis.SyntheticMarker;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.android.tools.r8.utils.structural.StructuralItem;
@@ -52,6 +53,7 @@
   private CfVersion initialClassFileVersion = null;
   private boolean deprecated = false;
   private KotlinClassLevelInfo kotlinInfo = getNoKotlinInfo();
+  private OptionalBool reachabilitySensitive = OptionalBool.unknown();
 
   private final ChecksumSupplier checksumSupplier;
 
@@ -160,6 +162,33 @@
     return this;
   }
 
+  /**
+   * Is the class reachability sensitive.
+   *
+   * <p>A class is reachability sensitive if the
+   * dalvik.annotation.optimization.ReachabilitySensitive annotation is on any field or method. When
+   * that is the case, dead reference elimination is disabled and locals are kept alive for their
+   * entire scope.
+   */
+  public boolean getOrComputeReachabilitySensitive(AppView<?> appView) {
+    if (reachabilitySensitive.isUnknown()) {
+      reachabilitySensitive = OptionalBool.of(internalComputeReachabilitySensitive(appView));
+    }
+    return reachabilitySensitive.isTrue();
+  }
+
+  private boolean internalComputeReachabilitySensitive(AppView<?> appView) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    for (DexEncodedMember<?, ?> member : members()) {
+      for (DexAnnotation annotation : member.annotations().annotations) {
+        if (annotation.annotation.type == dexItemFactory.annotationReachabilitySensitive) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   @Override
   public StructuralMapping<DexProgramClass> getStructuralMapping() {
     return DexProgramClass::specify;
@@ -750,25 +779,6 @@
     return deprecated;
   }
 
-  /**
-   * Is the class reachability sensitive.
-   *
-   * <p>A class is reachability sensitive if the
-   * dalvik.annotation.optimization.ReachabilitySensitive annotation is on any field or method. When
-   * that is the case, dead reference elimination is disabled and locals are kept alive for their
-   * entire scope.
-   */
-  public boolean hasReachabilitySensitiveAnnotation(DexItemFactory factory) {
-    for (DexEncodedMember<?, ?> member : members()) {
-      for (DexAnnotation annotation : member.annotations().annotations) {
-        if (annotation.annotation.type == factory.annotationReachabilitySensitive) {
-          return true;
-        }
-      }
-    }
-    return false;
-  }
-
   public static Iterable<DexProgramClass> asProgramClasses(
       Iterable<DexType> types, DexDefinitionSupplier definitions) {
     return () ->
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index f60161d..37d165b 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 
 /** Type representing a method definition in the programs compilation unit and its holder. */
 public final class ProgramMethod extends DexClassAndMethod
@@ -112,7 +113,7 @@
     MethodAccessFlags accessFlags = getAccessFlags();
     accessFlags.demoteFromAbstract();
     getDefinition().setApiLevelForCode(appView.computedMinApiLevel());
-    getDefinition().setCode(ThrowNullCode.get(), appView);
+    setCode(ThrowNullCode.get(), appView);
     getSimpleFeedback().markProcessed(getDefinition(), ConstraintWithTarget.ALWAYS);
     getSimpleFeedback().unsetOptimizationInfoForThrowNullMethod(this);
   }
@@ -178,4 +179,29 @@
   public KotlinMethodLevelInfo getKotlinInfo() {
     return getDefinition().getKotlinInfo();
   }
+
+  public boolean getOrComputeReachabilitySensitive(AppView<?> appView) {
+    return getHolder().getOrComputeReachabilitySensitive(appView);
+  }
+
+  public void setCode(Code newCode, AppView<?> appView) {
+    // If the locals are not kept, we might still need information to satisfy -keepparameternames.
+    // The information needs to be retrieved on the original code object before replacing it.
+    Code code = getDefinition().getCode();
+    Int2ReferenceMap<DebugLocalInfo> parameterInfo = getDefinition().getParameterInfo();
+    if (code != null
+        && code.isCfCode()
+        && !getDefinition().hasParameterInfo()
+        && !keepLocals(appView)) {
+      parameterInfo = code.collectParameterInfo(getDefinition(), appView);
+    }
+    getDefinition().setCode(newCode, parameterInfo);
+  }
+
+  public boolean keepLocals(AppView<?> appView) {
+    if (appView.testing().noLocalsTableOnInput) {
+      return false;
+    }
+    return appView.options().debug || getOrComputeReachabilitySensitive(appView);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index c0cb840..df258fe 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -225,7 +225,6 @@
                 IncompleteHorizontalClassMergerCode code =
                     (IncompleteHorizontalClassMergerCode) method.getDefinition().getCode();
                 method
-                    .getDefinition()
                     .setCode(
                         code.toCfCode(appView, method, horizontalClassMergerGraphLens), appView);
               });
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
index cf531ff..0ac3c7e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
@@ -78,7 +78,7 @@
       }
     }
     rewriteCode();
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   private void rewriteCode() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 5175138..01b08a8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -587,15 +587,15 @@
     }
   }
 
-  public boolean isConsistentSSA() {
-    isConsistentSSABeforeTypesAreCorrect();
+  public boolean isConsistentSSA(AppView<?> appView) {
+    isConsistentSSABeforeTypesAreCorrect(appView);
     assert verifyNoImpreciseOrBottomTypes();
     return true;
   }
 
-  public boolean isConsistentSSABeforeTypesAreCorrect() {
-    assert isConsistentGraph(true);
-    assert consistentBlockInstructions(true);
+  public boolean isConsistentSSABeforeTypesAreCorrect(AppView<?> appView) {
+    assert isConsistentGraph(appView, true);
+    assert consistentBlockInstructions(appView, true);
     assert consistentDefUseChains();
     assert validThrowingInstructions();
     assert noCriticalEdges();
@@ -628,16 +628,16 @@
     return true;
   }
 
-  public boolean isConsistentGraph() {
-    return isConsistentGraph(false);
+  public boolean isConsistentGraph(AppView<?> appView) {
+    return isConsistentGraph(appView, false);
   }
 
-  public boolean isConsistentGraph(boolean ssa) {
+  public boolean isConsistentGraph(AppView<?> appView, boolean ssa) {
     assert noColorsInUse();
     assert consistentBlockNumbering();
     assert consistentPredecessorSuccessors();
     assert consistentCatchHandlers();
-    assert consistentBlockInstructions(ssa);
+    assert consistentBlockInstructions(appView, ssa);
     assert consistentMetadata();
     assert !allThrowingInstructionsHavePositions || computeAllThrowingInstructionsHavePositions();
     return true;
@@ -829,12 +829,12 @@
     return true;
   }
 
-  private boolean consistentBlockInstructions(boolean ssa) {
+  private boolean consistentBlockInstructions(AppView<?> appView, boolean ssa) {
     boolean argumentsAllowed = true;
     for (BasicBlock block : blocks) {
       assert block.consistentBlockInstructions(
           argumentsAllowed,
-          options.debug || method().getOptimizationInfo().isReachabilitySensitive(),
+          options.debug || context().getOrComputeReachabilitySensitive(appView),
           ssa);
       argumentsAllowed = false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index e5f3722..efec2dd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -84,7 +84,7 @@
   public DeadInstructionResult canBeDeadCode(AppView<?> appView, IRCode code) {
     InternalOptions options = appView.options();
     if (options.debug
-        || code.context().getDefinition().getOptimizationInfo().isReachabilitySensitive()
+        || code.context().getOrComputeReachabilitySensitive(appView)
         || code.getConversionOptions().isGeneratingClassFiles()) {
       return DeadInstructionResult.notDead();
     }
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 fa3c642..4039c58 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
@@ -163,7 +163,7 @@
         reachedFixpoint = !phiOptimizations.optimize(code);
       }
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
     // Insert reads for uninitialized read blocks to ensure correct stack maps.
     Set<UninitializedThisLocalRead> uninitializedThisLocalReads =
         insertUninitializedThisLocalReads();
@@ -181,7 +181,7 @@
 
     if (code.getConversionOptions().isPeepholeOptimizationsEnabled()) {
       for (int i = 0; i < PEEPHOLE_OPTIMIZATION_PASSES; i++) {
-        CodeRewriter.collapseTrivialGotos(code);
+        CodeRewriter.collapseTrivialGotos(appView, code);
         PeepholeOptimizer.removeIdenticalPredecessorBlocks(code, registerAllocator);
         PeepholeOptimizer.shareIdenticalBlockSuffix(
             code, registerAllocator, SUFFIX_SHARING_OVERHEAD);
@@ -190,7 +190,7 @@
 
     rewriteIincPatterns();
 
-    CodeRewriter.collapseTrivialGotos(code);
+    CodeRewriter.collapseTrivialGotos(appView, code);
     DexBuilder.removeRedundantDebugPositions(code);
     CfCode code = buildCfCode();
     assert verifyInvokeInterface(code, appView);
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 301de1a..7d83c86 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
@@ -501,7 +501,7 @@
   }
 
   public boolean isDebugMode() {
-    return appView.options().debug || getMethod().getOptimizationInfo().isReachabilitySensitive();
+    return appView.options().debug || getProgramMethod().getOrComputeReachabilitySensitive(appView);
   }
 
   public Int2ReferenceSortedMap<BlockInfo> getCFG() {
@@ -739,7 +739,7 @@
       StringSwitchConverter.convertToStringSwitchInstructions(ir, appView.dexItemFactory());
     }
 
-    assert ir.isConsistentSSA();
+    assert ir.isConsistentSSA(appView);
 
     // Clear the code so we don't build multiple times.
     source.clear();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index b51a072..e9633f7 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
@@ -468,7 +468,6 @@
       D8CfInstructionDesugaringEventConsumer desugaringEventConsumer,
       D8MethodProcessor methodProcessor,
       InterfaceProcessor interfaceProcessor) {
-    boolean isReachabilitySensitive = clazz.hasReachabilitySensitiveAnnotation(options.itemFactory);
     // When converting all methods on a class always convert <clinit> first.
     ProgramMethod classInitializer = clazz.getProgramClassInitializer();
 
@@ -479,17 +478,12 @@
     //  the need to copy the method list.
     List<ProgramMethod> methods = ListUtils.newArrayList(clazz::forEachProgramMethod);
     if (classInitializer != null) {
-      classInitializer
-          .getDefinition()
-          .getMutableOptimizationInfo()
-          .setReachabilitySensitive(isReachabilitySensitive);
       methodProcessor.processMethod(classInitializer, desugaringEventConsumer);
     }
 
     for (ProgramMethod method : methods) {
       if (!method.getDefinition().isClassInitializer()) {
         DexEncodedMethod definition = method.getDefinition();
-        definition.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive);
         methodProcessor.processMethod(method, desugaringEventConsumer);
         if (interfaceProcessor != null) {
           interfaceProcessor.processMethod(method, desugaringEventConsumer);
@@ -623,9 +617,6 @@
     // Desugaring happens in the enqueuer.
     assert instructionDesugaring.isEmpty();
 
-    DexApplication application = appView.appInfo().app();
-
-    computeReachabilitySensitivity(application);
     workaroundAbstractMethodOnNonAbstractClassVerificationBug(executorService);
 
     // The process is in two phases in general.
@@ -846,18 +837,6 @@
     return onWaveDoneActions != null;
   }
 
-  private void computeReachabilitySensitivity(DexApplication application) {
-    application
-        .classes()
-        .forEach(
-            c -> {
-              if (c.hasReachabilitySensitiveAnnotation(options.itemFactory)) {
-                c.methods()
-                    .forEach(m -> m.getMutableOptimizationInfo().setReachabilitySensitive(true));
-              }
-            });
-  }
-
   private void processSynthesizedServiceLoaderMethods(
       List<ProgramMethod> serviceLoadMethods, ExecutorService executorService)
       throws ExecutionException {
@@ -882,18 +861,16 @@
 
   /**
    * This will replace the Dex code in the method with the Dex code generated from the provided IR.
-   * <p>
-   * This method is *only* intended for testing, where tests manipulate the IR and need runnable Dex
-   * code.
    *
-   * @param method the method to replace code for
+   * <p>This method is *only* intended for testing, where tests manipulate the IR and need runnable
+   * Dex code.
+   *
    * @param code the IR code for the method
    */
-  public void replaceCodeForTesting(DexEncodedMethod method, IRCode code) {
-    if (Log.ENABLED) {
-      Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code);
-    }
-    assert code.isConsistentSSA();
+  public void replaceCodeForTesting(IRCode code) {
+    ProgramMethod method = code.context();
+    DexEncodedMethod definition = method.getDefinition();
+    assert code.isConsistentSSA(appView);
     Timing timing = Timing.empty();
     deadCodeRemover.run(code, timing);
     method.setCode(
@@ -901,8 +878,11 @@
             .finalizeCode(code, BytecodeMetadataProvider.empty(), timing),
         appView);
     if (Log.ENABLED) {
-      Log.debug(getClass(), "Resulting dex code for %s:\n%s",
-          method.toSourceString(), logCode(options, method));
+      Log.debug(
+          getClass(),
+          "Resulting dex code for %s:\n%s",
+          method.toSourceString(),
+          logCode(options, definition));
     }
   }
 
@@ -1101,7 +1081,7 @@
       timing.end();
     }
 
-    boolean isDebugMode = options.debug || method.getOptimizationInfo().isReachabilitySensitive();
+    boolean isDebugMode = options.debug || context.getOrComputeReachabilitySensitive(appView);
 
     if (isDebugMode) {
       codeRewriter.simplifyDebugLocals(code);
@@ -1142,7 +1122,7 @@
     // check. In the latter case, the type checker should be extended to detect the issue such that
     // we will return with a throw-null method above.
     assert code.verifyTypes(appView);
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
 
     openClosedInterfacesAnalysis.analyze(context, code);
 
@@ -1176,7 +1156,7 @@
       timing.begin("Decouple identifier-name strings");
       identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(code);
       timing.end();
-      assert code.isConsistentSSA();
+      assert code.isConsistentSSA(appView);
     }
 
     if (memberValuePropagation != null) {
@@ -1259,7 +1239,7 @@
           .optimize(code, feedback, methodProcessor, methodProcessingContext);
       timing.end();
       previous = printMethod(code, "IR after class library method optimizer (SSA)", previous);
-      assert code.isConsistentSSA();
+      assert code.isConsistentSSA(appView);
     }
 
     assert code.verifyTypes(appView);
@@ -1366,7 +1346,7 @@
     // as a result of those simplifications. The following optimizations could reveal more
     // dead code which is removed right before register allocation in performRegisterAllocation.
     deadCodeRemover.run(code, timing);
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
 
     previous = printMethod(code, "IR after dead code removal (SSA)", previous);
 
@@ -1399,7 +1379,7 @@
                       // always uses a force inlining oracle for inlining.
                       -1)));
       timing.end();
-      assert code.isConsistentSSA();
+      assert code.isConsistentSSA(appView);
       assert code.verifyTypes(appView);
     }
 
@@ -1451,7 +1431,7 @@
     // Insert code to log arguments if requested.
     if (options.methodMatchesLogArgumentsFilter(method) && !method.isProcessed()) {
       codeRewriter.logArgumentTypes(method, code);
-      assert code.isConsistentSSA();
+      assert code.isConsistentSSA(appView);
     }
 
     previous = printMethod(code, "IR after argument type logging (SSA)", previous);
@@ -1479,7 +1459,7 @@
       timing.begin("Remove assume instructions");
       CodeRewriter.removeAssumeInstructions(appView, code);
       timing.end();
-      assert code.isConsistentSSA();
+      assert code.isConsistentSSA(appView);
 
       // TODO(b/214496607): Remove when dynamic types are safe w.r.t. interface assignment rules.
       codeRewriter.rewriteMoveResult(code);
@@ -1615,7 +1595,7 @@
       OptimizationFeedback feedback,
       BytecodeMetadataProvider bytecodeMetadataProvider,
       Timing timing) {
-    DexEncodedMethod method = code.method();
+    ProgramMethod method = code.context();
     method.setCode(
         new IRToCfFinalizer(appView, deadCodeRemover)
             .finalizeCode(code, bytecodeMetadataProvider, timing),
@@ -1628,13 +1608,14 @@
       OptimizationFeedback feedback,
       BytecodeMetadataProvider bytecodeMetadataProvider,
       Timing timing) {
-    DexEncodedMethod method = code.method();
+    ProgramMethod method = code.context();
+    DexEncodedMethod definition = method.getDefinition();
     method.setCode(
         new IRToDexFinalizer(appView, codeRewriter, deadCodeRemover)
             .finalizeCode(code, bytecodeMetadataProvider, timing),
         appView);
     markProcessed(code, feedback);
-    updateHighestSortingStrings(method);
+    updateHighestSortingStrings(definition);
   }
 
   public void markProcessed(IRCode code, OptimizationFeedback feedback) {
@@ -1652,8 +1633,7 @@
       return false;
     }
     DexEncodedMethod definition = method.getDefinition();
-    if (definition.isClassInitializer()
-        || definition.getOptimizationInfo().isReachabilitySensitive()) {
+    if (definition.isClassInitializer() || method.getOrComputeReachabilitySensitive(appView)) {
       return false;
     }
     KeepMethodInfo keepInfo = appView.getKeepInfo(method);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRToDexFinalizer.java b/src/main/java/com/android/tools/r8/ir/conversion/IRToDexFinalizer.java
index c709bf5..70fd16a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRToDexFinalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRToDexFinalizer.java
@@ -63,14 +63,14 @@
     if (code.getConversionOptions().isPeepholeOptimizationsEnabled()) {
       timing.begin("Peephole optimize");
       for (int i = 0; i < PEEPHOLE_OPTIMIZATION_PASSES; i++) {
-        CodeRewriter.collapseTrivialGotos(code);
-        PeepholeOptimizer.optimize(code, registerAllocator);
+        CodeRewriter.collapseTrivialGotos(appView, code);
+        PeepholeOptimizer.optimize(appView, code, registerAllocator);
       }
       timing.end();
     }
     timing.begin("Clean up");
     CodeRewriter.removeUnneededMovesOnExitingPaths(code, registerAllocator);
-    CodeRewriter.collapseTrivialGotos(code);
+    CodeRewriter.collapseTrivialGotos(appView, code);
     timing.end();
     if (Log.ENABLED) {
       Log.debug(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 309cf22..6c0a865 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -809,7 +809,7 @@
     // Finalize cast and null check insertion.
     interfaceTypeToClassTypeRewriterHelper.processWorklist();
 
-    assert code.isConsistentSSABeforeTypesAreCorrect();
+    assert code.isConsistentSSABeforeTypesAreCorrect(appView);
   }
 
   // Applies the prototype changes of the current method to the argument instructions:
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 8ad701f..c1a7b55 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -109,8 +109,6 @@
 
   void unsetNonNullParamOrThrow(ProgramMethod method);
 
-  void unsetReachabilitySensitive(ProgramMethod method);
-
   void unsetReturnedArgument(ProgramMethod method);
 
   void unsetReturnValueOnlyDependsOnArguments(ProgramMethod method);
@@ -135,7 +133,6 @@
       unsetNeverReturnsNormally(method);
       unsetNonNullParamOnNormalExits(method);
       unsetNonNullParamOrThrow(method);
-      unsetReachabilitySensitive(method);
       unsetReturnedArgument(method);
       unsetReturnValueOnlyDependsOnArguments(method);
       unsetSimpleInliningConstraint(method);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
index 6c4a72d..ecaeac2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
@@ -101,7 +101,7 @@
       identifierNameStringMarker.decoupleIdentifierNameStringsInBlocks(code, newBlocksWithStrings);
     }
 
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   // Returns true if minification is enabled and the switch value is guaranteed to be a class name.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 9aada99..4fecb6d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -221,7 +221,6 @@
               info -> {
                 ProgramMethod newDirectMethod = info.getNewDirectMethod();
                 newDirectMethod
-                    .getDefinition()
                     .setCode(info.getVirtualMethod().getDefinition().getCode(), appView);
               });
 
@@ -232,7 +231,6 @@
           .forEach(
               info -> {
                 info.getVirtualMethod()
-                    .getDefinition()
                     .setCode(info.getVirtualMethodCode(), appView);
                 needsProcessing.accept(info.getVirtualMethod());
               });
@@ -433,7 +431,6 @@
       pendingInvokeSpecialBridges.forEach(
           info ->
               info.getVirtualMethod()
-                  .getDefinition()
                   .setCode(info.getVirtualMethodCode(), appView));
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
index 450cdb5..a0f8961 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
@@ -209,7 +209,7 @@
                 () -> context.createUniqueContext(clazz))
             .generateCfCode();
     DexEncodedMethod newMethod = wrapperSynthesizor.newSynthesizedMethod(methodToInstall, cfCode);
-    newMethod.setCode(cfCode, appView);
+    newMethod.setCode(cfCode, DexEncodedMethod.NO_PARAMETER_INFO);
     if (originalMethod.isLibraryMethodOverride().isTrue()) {
       newMethod.setLibraryMethodOverride(OptionalBool.TRUE);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 5d14741..f963a21 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -175,8 +175,8 @@
       DexEncodedMethod.setDebugInfoWithFakeThisParameter(
           code, companion.getReference().getArity(), appView);
     }
-    companion.getDefinition().setCode(code, appView);
-    definition.setCode(InvalidCode.getInstance(), appView);
+    companion.setCode(code, appView);
+    method.setCode(InvalidCode.getInstance(), appView);
   }
 
   private void clearDirectMethods(DexProgramClass iface) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index 3f17800..a08a5ba 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -239,8 +239,9 @@
             // Will be traced by the enqueuer.
             .disableAndroidApiLevelCheck()
             .build();
-    encodedMethod.setCode(provider.generateCfCode(), appView);
-    return new ProgramMethod(clazz, encodedMethod);
+    ProgramMethod result = new ProgramMethod(clazz, encodedMethod);
+    result.setCode(provider.generateCfCode(), appView);
+    return result;
   }
 
   private DexMethod ensureEqualsRecord(
@@ -462,17 +463,13 @@
     MethodAccessFlags methodAccessFlags =
         MethodAccessFlags.fromSharedAccessFlags(
             Constants.ACC_SYNTHETIC | Constants.ACC_PROTECTED, true);
-    DexEncodedMethod init =
-        DexEncodedMethod.syntheticBuilder()
-            .setMethod(factory.recordMembers.constructor)
-            .setAccessFlags(methodAccessFlags)
-            .setCode(null)
-            // Will be traced by the enqueuer.
-            .disableAndroidApiLevelCheck()
-            .build();
-    init.setCode(
-        new CallObjectInitCfCodeProvider(appView, factory.recordTagType).generateCfCode(), appView);
-    return init;
+    return DexEncodedMethod.syntheticBuilder()
+        .setMethod(factory.recordMembers.constructor)
+        .setAccessFlags(methodAccessFlags)
+        .setCode(new CallObjectInitCfCodeProvider(appView, factory.recordTagType).generateCfCode())
+        // Will be traced by the enqueuer.
+        .disableAndroidApiLevelCheck()
+        .build();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index 0da7886..72b8d6a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -340,7 +340,7 @@
     if (enabled) {
       timing.begin("Rewrite assertions");
       runInternal(method, code);
-      assert code.isConsistentSSA();
+      assert code.isConsistentSSA(appView);
       timing.end();
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index 3551295..ef1b959 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -137,7 +137,7 @@
     }
 
     ProgramMethod context = code.context();
-    if (context.getDefinition().getOptimizationInfo().isReachabilitySensitive()) {
+    if (context.getOrComputeReachabilitySensitive(appView)) {
       return ClassInitializerDefaultsResult.empty();
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 2289a72..05907fa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -386,7 +386,7 @@
         new TypeAnalysis(appView).narrowing(affectedValues);
       }
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   public static boolean isFallthroughBlock(BasicBlock block) {
@@ -954,7 +954,7 @@
     if (!affectedValues.isEmpty()) {
       new TypeAnalysis(appView).narrowing(affectedValues);
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
     return !affectedValues.isEmpty();
   }
 
@@ -1160,8 +1160,8 @@
    * rewrite fallthrough targets as that would require block reordering and the transformation only
    * makes sense after SSA destruction where there are no phis.
    */
-  public static void collapseTrivialGotos(IRCode code) {
-    assert code.isConsistentGraph();
+  public static void collapseTrivialGotos(AppView<?> appView, IRCode code) {
+    assert code.isConsistentGraph(appView);
     List<BasicBlock> blocksToRemove = new ArrayList<>();
     // Rewrite all non-fallthrough targets to the end of trivial goto chains and remove
     // first round of trivial goto blocks.
@@ -1199,7 +1199,7 @@
       code.removeBlocks(blocksToRemove);
     }
     assert removedTrivialGotos(code);
-    assert code.isConsistentGraph();
+    assert code.isConsistentGraph(appView);
   }
 
   private boolean checkArgumentType(InvokeMethod invoke, int argumentIndex) {
@@ -1288,7 +1288,7 @@
     if (!affectedValues.isEmpty()) {
       new TypeAnalysis(appView).narrowing(affectedValues);
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
     return changed;
   }
 
@@ -1379,7 +1379,7 @@
         typeAnalysis.narrowing(affectedValues);
       }
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   // Returns true if the given check-cast instruction was removed.
@@ -1657,7 +1657,7 @@
         }
       }
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   /**
@@ -1736,7 +1736,7 @@
       }
     }
 
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   // Check if a binop can be represented in the binop/lit8 or binop/lit16 form.
@@ -1919,7 +1919,7 @@
       }
     }
 
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   private void forEachUse(Instruction instruction, Consumer<Value> fn) {
@@ -2244,7 +2244,7 @@
         } while (block != null);
       }
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   // TODO(mikaelpeltier) Manage that from and to instruction do not belong to the same block.
@@ -2469,7 +2469,7 @@
       }
     }
     code.returnMarkingColor(noCandidate);
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   static class ControlFlowSimplificationResult {
@@ -2545,7 +2545,7 @@
     if (!affectedValues.isEmpty()) {
       new TypeAnalysis(appView).narrowing(affectedValues);
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
     return new ControlFlowSimplificationResult(!affectedValues.isEmpty(), simplified);
   }
 
@@ -2901,7 +2901,7 @@
     if (changed) {
       code.removeAllDeadAndTrivialPhis();
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   private static Long2ReferenceMap<List<ConstNumber>> getConstantsByValue(IRCode code) {
@@ -3116,7 +3116,7 @@
     if (!affectedValues.isEmpty()) {
       new TypeAnalysis(appView).narrowing(affectedValues);
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   /* Identify simple diamond shapes converting boolean true/false to 1/0. We consider the forms:
@@ -3378,7 +3378,7 @@
 
       phiUsers.forEach(Phi::removeTrivialPhi);
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   public void rewriteAssertionErrorTwoArgumentConstructor(IRCode code, InternalOptions options) {
@@ -3427,7 +3427,7 @@
         }
       }
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index 912a030..8839c13 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -270,7 +270,7 @@
       codeRewriter.simplifyIf(code);
     }
 
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   private static void insertCanonicalizedConstant(IRCode code, Instruction canonicalizedConstant) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 5dbfaba..caacb26 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -58,7 +58,7 @@
       }
     } while (codeRewriter.simplifyIf(code).anySimplifications()
         || removeUnneededCatchHandlers(code));
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
 
     timing.end();
   }
@@ -210,7 +210,7 @@
     if (mayHaveIntroducedUnreachableBlocks) {
       code.removeUnreachableBlocks();
     }
-    assert code.isConsistentGraph();
+    assert code.isConsistentGraph(appView);
     return mayHaveIntroducedUnreachableBlocks;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 2ef8dd2..6450496 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -315,7 +315,7 @@
     if (!affectedValues.isEmpty()) {
       new TypeAnalysis(appView).narrowing(affectedValues);
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   /** This rebinds invoke-super instructions to their most specific target. */
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 28530d3..4b3bb6f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -271,7 +271,7 @@
     }
 
     code.removeAllDeadAndTrivialPhis();
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   private boolean isIdempotentLibraryMethodInvoke(InvokeMethod invoke) {
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 c458574..4ea98bc 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
@@ -704,7 +704,7 @@
       if (options.testing.inlineeIrModifier != null) {
         options.testing.inlineeIrModifier.accept(code);
       }
-      assert code.isConsistentSSA();
+      assert code.isConsistentSSA(appView);
       return new InlineeWithReason(code, reason);
     }
 
@@ -1105,7 +1105,7 @@
     classInitializationAnalysis.finish();
     code.removeBlocks(blocksToRemove);
     code.removeAllDeadAndTrivialPhis();
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   private boolean tryInlineMethodWithoutSideEffects(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 08edc70..6e3c12d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -510,7 +510,7 @@
     if (!affectedValues.isEmpty()) {
       new TypeAnalysis(appView).narrowing(affectedValues);
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
     assert code.verifyTypes(appView);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java
index 0951ff9..c82a122 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java
@@ -41,7 +41,7 @@
     }
     if (loopRemoved) {
       code.removeAllDeadAndTrivialPhis();
-      assert code.isConsistentSSA();
+      assert code.isConsistentSSA(appView);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
index ffda2f3..0b199bc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.ConstNumber;
@@ -36,12 +37,13 @@
   /**
    * Perform optimizations of the code with register assignments provided by the register allocator.
    */
-  public static void optimize(IRCode code, LinearScanRegisterAllocator allocator) {
+  public static void optimize(
+      AppView<?> appView, IRCode code, LinearScanRegisterAllocator allocator) {
     removeIdenticalPredecessorBlocks(code, allocator);
     removeRedundantInstructions(code, allocator);
     shareIdenticalBlockPrefix(code, allocator);
     shareIdenticalBlockSuffix(code, allocator, 0);
-    assert code.isConsistentGraph();
+    assert code.isConsistentGraph(appView);
   }
 
   /** Identify common prefixes in successor blocks and share them. */
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index d758988..5966a8c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -413,7 +413,7 @@
     }
     processInstructionsToRemove();
     assumeRemover.removeMarkedInstructions().finish();
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   private void processInstructionsToRemove() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 683cf92..879646e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -74,7 +74,7 @@
     if (!affectedValues.isEmpty()) {
       new TypeAnalysis(appView).narrowing(affectedValues);
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   private static BiConsumer<DexType, DexClass> rewriteSingleGetClassOrForNameToConstClass(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 442cb96..ccbd132 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -229,7 +229,7 @@
         assumeRemover.removeMarkedInstructions();
         code.removeAllDeadAndTrivialPhis(affectedValues);
         assumeRemover.finish();
-        assert code.isConsistentSSA();
+        assert code.isConsistentSSA(appView);
         rootsIterator.remove();
         repeat = true;
       }
@@ -253,8 +253,7 @@
       codeRewriter.simplifyControlFlow(code);
       // If a method was inlined we may see more trivial computation/conversion of String.
       boolean isDebugMode =
-          appView.options().debug
-              || method.getDefinition().getOptimizationInfo().isReachabilitySensitive();
+          appView.options().debug || method.getOrComputeReachabilitySensitive(appView);
       if (!isDebugMode) {
         // Reflection/string optimization 3. trivial conversion/computation on const-string
         stringOptimizer.computeTrivialOperationsOnConstString(code);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index f223300..b6b8ca4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -120,7 +120,7 @@
     if (unboxedEnumsData.isEmpty()) {
       return Sets.newIdentityHashSet();
     }
-    assert code.isConsistentSSABeforeTypesAreCorrect();
+    assert code.isConsistentSSABeforeTypesAreCorrect(appView);
     ProgramMethod context = code.context();
     Map<Instruction, DexType> convertedEnums = createInitialConvertedEnums(code, prototypeChanges);
     Set<Phi> affectedPhis = Sets.newIdentityHashSet();
@@ -351,7 +351,7 @@
         }
       }
     }
-    assert code.isConsistentSSABeforeTypesAreCorrect();
+    assert code.isConsistentSSABeforeTypesAreCorrect(appView);
     return affectedPhis;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index 293d93f..50e3461 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -175,7 +175,7 @@
     if (!affectedValues.isEmpty()) {
       new TypeAnalysis(appView).narrowing(affectedValues);
     }
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 9faaebc..c9f6819 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -106,11 +106,6 @@
   }
 
   @Override
-  public boolean isReachabilitySensitive() {
-    return false;
-  }
-
-  @Override
   public boolean returnsArgument() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index ffcad46..c36ee4d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -54,8 +54,6 @@
 
   public abstract boolean hasBeenInlinedIntoSingleCallSite();
 
-  public abstract boolean isReachabilitySensitive();
-
   public abstract boolean returnsArgument();
 
   public abstract int getReturnedArgument();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index b810885..df68f40 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -88,14 +88,9 @@
   private static final int HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG = 0x4;
   private static final int MAY_HAVE_SIDE_EFFECT_FLAG = 0x8;
   private static final int RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG = 0x10;
-  private static final int UNUSED_FLAG_1 = 0x20;
-  private static final int NEVER_RETURNS_NORMALLY_FLAG = 0x40;
-  private static final int UNUSED_FLAG_2 = 0x80;
-  private static final int UNUSED_FLAG_3 = 0x100;
-  private static final int UNUSED_FLAG_4 = 0x200;
-  private static final int INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG = 0x400;
-  private static final int REACHABILITY_SENSITIVE_FLAG = 0x800;
-  private static final int RETURN_VALUE_HAS_BEEN_PROPAGATED_FLAG = 0x1000;
+  private static final int NEVER_RETURNS_NORMALLY_FLAG = 0x20;
+  private static final int INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG = 0x80;
+  private static final int RETURN_VALUE_HAS_BEEN_PROPAGATED_FLAG = 0x100;
 
   private static final int DEFAULT_FLAGS;
 
@@ -114,19 +109,12 @@
     defaultFlags |=
         BooleanUtils.intValue(defaultOptInfo.returnValueOnlyDependsOnArguments())
             * RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG;
-    defaultFlags |= 0 * UNUSED_FLAG_1;
     defaultFlags |=
         BooleanUtils.intValue(defaultOptInfo.neverReturnsNormally()) * NEVER_RETURNS_NORMALLY_FLAG;
-    defaultFlags |= 0 * UNUSED_FLAG_2;
-    defaultFlags |= 0 * UNUSED_FLAG_3;
-    defaultFlags |= 0 * UNUSED_FLAG_4;
     defaultFlags |=
         BooleanUtils.intValue(defaultOptInfo.isInitializerEnablingJavaVmAssertions())
             * INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG;
     defaultFlags |=
-        BooleanUtils.intValue(defaultOptInfo.isReachabilitySensitive())
-            * REACHABILITY_SENSITIVE_FLAG;
-    defaultFlags |=
         BooleanUtils.intValue(defaultOptInfo.returnValueHasBeenPropagated())
             * RETURN_VALUE_HAS_BEEN_PROPAGATED_FLAG;
     DEFAULT_FLAGS = defaultFlags;
@@ -381,11 +369,6 @@
   }
 
   @Override
-  public boolean isReachabilitySensitive() {
-    return isFlagSet(REACHABILITY_SENSITIVE_FLAG);
-  }
-
-  @Override
   public boolean returnsArgument() {
     return returnedArgument != -1;
   }
@@ -515,14 +498,6 @@
     return isFlagSet(RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG);
   }
 
-  public void setReachabilitySensitive(boolean reachabilitySensitive) {
-    setFlag(REACHABILITY_SENSITIVE_FLAG, reachabilitySensitive);
-  }
-
-  void unsetReachabilitySensitive() {
-    clearFlag(REACHABILITY_SENSITIVE_FLAG);
-  }
-
   void setSimpleInliningConstraint(SimpleInliningConstraint constraint) {
     this.simpleInliningConstraint = constraint;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 8853607..af6cf62 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -364,11 +364,6 @@
   }
 
   @Override
-  public synchronized void unsetReachabilitySensitive(ProgramMethod method) {
-    getMethodOptimizationInfoForUpdating(method).unsetReachabilitySensitive();
-  }
-
-  @Override
   public synchronized void unsetReturnedArgument(ProgramMethod method) {
     getMethodOptimizationInfoForUpdating(method).unsetReturnedArgument();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index d15bb00..3f46ce1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -176,9 +176,6 @@
   public void unsetNonNullParamOrThrow(ProgramMethod method) {}
 
   @Override
-  public void unsetReachabilitySensitive(ProgramMethod method) {}
-
-  @Override
   public void unsetReturnedArgument(ProgramMethod method) {}
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 5b65a8a..7a6a3ce 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -305,12 +305,6 @@
   }
 
   @Override
-  public void unsetReachabilitySensitive(ProgramMethod method) {
-    withMutableMethodOptimizationInfo(
-        method, MutableMethodOptimizationInfo::unsetReachabilitySensitive);
-  }
-
-  @Override
   public void unsetReturnedArgument(ProgramMethod method) {
     withMutableMethodOptimizationInfo(method, MutableMethodOptimizationInfo::unsetReturnedArgument);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index efd09bc..b851f1e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -816,7 +816,7 @@
       if (!affectedValues.isEmpty()) {
         new TypeAnalysis(appView).narrowing(affectedValues);
       }
-      assert code.isConsistentSSA();
+      assert code.isConsistentSSA(appView);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
index 8c6a531..93f6242 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -133,7 +133,7 @@
     if (abstractParentReturnValue == abstractReturnValue) {
       // The parent method is already guaranteed to return the same value.
     } else if (isClassAccessible(method.getHolder(), parentMethod, appView).isTrue()) {
-      parentMethodDefinition.setCode(
+      parentMethod.setCode(
           parentMethodDefinition.buildInstanceOfCode(
               method.getHolderType(), abstractParentReturnValue.isTrue(), appView.options()),
           appView);
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 54de6c0..af1ed03 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -207,7 +207,7 @@
   public void allocateRegisters() {
     // There are no linked values prior to register allocation.
     assert noLinkedValues();
-    assert code.isConsistentSSA();
+    assert code.isConsistentSSA(appView);
     if (this.code.method().accessFlags.isBridge() && implementationIsBridge(this.code)) {
       transformBridgeMethod();
     }
@@ -216,7 +216,7 @@
     insertRangeInvokeMoves();
     ImmutableList<BasicBlock> blocks = computeLivenessInformation();
     performAllocation();
-    assert code.isConsistentGraph();
+    assert code.isConsistentGraph(appView);
     if (Log.ENABLED) {
       Log.debug(this.getClass(), toString());
     }
@@ -227,7 +227,7 @@
     // and we do not actually want locals information in the output.
     if (options().debug) {
       computeDebugInfo(code, blocks, liveIntervals, this, liveAtEntrySets);
-    } else if (code.method().getOptimizationInfo().isReachabilitySensitive()) {
+    } else if (code.context().getOrComputeReachabilitySensitive(appView)) {
       InstructionListIterator it = code.instructionListIterator();
       while (it.hasNext()) {
         Instruction instruction = it.next();
@@ -1642,7 +1642,7 @@
     // Set all free positions for possible registers to max integer.
     RegisterPositions freePositions = new RegisterPositionsImpl(registerConstraint + 1);
 
-    if ((options().debug || code.method().getOptimizationInfo().isReachabilitySensitive())
+    if ((options().debug || code.context().getOrComputeReachabilitySensitive(appView))
         && !code.method().accessFlags.isStatic()) {
       // If we are generating debug information or if the method is reachability sensitive,
       // we pin the this value register. The debugger expects to always be able to find it in
@@ -2555,7 +2555,7 @@
   }
 
   private void computeLiveRanges() {
-    computeLiveRanges(options(), code, liveAtEntrySets, liveIntervals);
+    computeLiveRanges(appView, code, liveAtEntrySets, liveIntervals);
     // Art VMs before Android M assume that the register for the receiver never changes its value.
     // This assumption is used during verification. Allowing the receiver register to be
     // overwritten can therefore lead to verification errors. If we could be targeting one of these
@@ -2579,7 +2579,7 @@
 
   /** Compute live ranges based on liveAtEntry sets for all basic blocks. */
   public static void computeLiveRanges(
-      InternalOptions options,
+      AppView<?> appView,
       IRCode code,
       Map<BasicBlock, LiveAtEntrySets> liveAtEntrySets,
       List<LiveIntervals> liveIntervals) {
@@ -2693,7 +2693,7 @@
             }
           }
         }
-        if (options.debug || code.method().getOptimizationInfo().isReachabilitySensitive()) {
+        if (appView.options().debug || code.context().getOrComputeReachabilitySensitive(appView)) {
           // In debug mode, or if the method is reachability sensitive, extend the live range
           // to cover the full scope of a local variable (encoded as debug values).
           int number = instruction.getNumber();
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
index 5550d52..16ea222 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
@@ -235,7 +235,6 @@
 
     // Now update the code of the bridge method chosen as representative.
     representative
-        .getDefinition()
         .setCode(createCodeForVirtualBridge(representative, methodToInvoke), appView);
     feedback.setBridgeInfo(representative.getDefinition(), new VirtualBridgeInfo(methodToInvoke));
 
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 22932f6..a85ff88 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -40,6 +40,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.OutlineCallerPosition;
 import com.android.tools.r8.ir.code.Position.OutlineCallerPosition.OutlineCallerPositionBuilder;
@@ -493,7 +494,7 @@
     for (DexProgramClass clazz : application.classes()) {
       boolean isSyntheticClass = appView.getSyntheticItems().isSyntheticClass(clazz);
 
-      IdentityHashMap<DexString, List<DexEncodedMethod>> methodsByRenamedName =
+      IdentityHashMap<DexString, List<ProgramMethod>> methodsByRenamedName =
           groupMethodsByRenamedName(appView.graphLens(), namingLens, clazz);
 
       // At this point we don't know if we really need to add this class to the builder.
@@ -538,7 +539,7 @@
       List<DexString> renamedMethodNames = new ArrayList<>(methodsByRenamedName.keySet());
       renamedMethodNames.sort(DexString::compareTo);
       for (DexString methodName : renamedMethodNames) {
-        List<DexEncodedMethod> methods = methodsByRenamedName.get(methodName);
+        List<ProgramMethod> methods = methodsByRenamedName.get(methodName);
         if (methods.size() > 1) {
           // If there are multiple methods with the same name (overloaded) then sort them for
           // deterministic behaviour: the algorithm will assign new line numbers in this order.
@@ -566,26 +567,27 @@
             new KotlinInlineFunctionPositionRemapper(
                 appView, positionRemapper, cfLineToMethodMapper);
 
-        for (DexEncodedMethod method : methods) {
-          kotlinRemapper.currentMethod = method;
+        for (ProgramMethod method : methods) {
+          DexEncodedMethod definition = method.getDefinition();
+          kotlinRemapper.currentMethod = definition;
           List<MappedPosition> mappedPositions;
-          Code code = method.getCode();
+          Code code = definition.getCode();
           boolean canUseDexPc =
-              methods.size() == 1 && representation.useDexPcEncoding(clazz, method);
+              methods.size() == 1 && representation.useDexPcEncoding(clazz, definition);
           if (code != null) {
             if (code.isDexCode() && doesContainPositions(code.asDexCode())) {
               if (canUseDexPc) {
                 mappedPositions =
                     optimizeDexCodePositionsForPc(
-                        method, appView, kotlinRemapper, pcBasedDebugInfo);
+                        definition, appView, kotlinRemapper, pcBasedDebugInfo);
               } else {
                 mappedPositions =
                     optimizeDexCodePositions(
-                        method, appView, kotlinRemapper, identityMapping, methods.size() != 1);
+                        definition, appView, kotlinRemapper, identityMapping, methods.size() != 1);
               }
             } else if (code.isCfCode()
                 && doesContainPositions(code.asCfCode())
-                && !appView.isCfByteCodePassThrough(method)) {
+                && !appView.isCfByteCodePassThrough(definition)) {
               mappedPositions = optimizeCfCodePositions(method, kotlinRemapper, appView);
             } else {
               mappedPositions = new ArrayList<>();
@@ -603,7 +605,7 @@
           String obfuscatedName = obfuscatedNameDexString.toString();
 
           List<MappingInformation> methodMappingInfo = new ArrayList<>();
-          if (method.isD8R8Synthesized()) {
+          if (definition.isD8R8Synthesized()) {
             methodMappingInfo.add(CompilerSynthesizedMappingInformation.builder().build());
           }
 
@@ -613,8 +615,8 @@
               && obfuscatedNameDexString == originalMethod.name
               && originalMethod.holder == originalType) {
             assert appView.options().lineNumberOptimization == LineNumberOptimization.OFF
-                || !doesContainPositions(method)
-                || appView.isCfByteCodePassThrough(method);
+                || !doesContainPositions(definition)
+                || appView.isCfByteCodePassThrough(definition);
             continue;
           }
 
@@ -691,8 +693,8 @@
               }
             }
             Range obfuscatedRange;
-            if (method.getCode().isDexCode()
-                && method.getCode().asDexCode().getDebugInfo()
+            if (definition.getCode().isDexCode()
+                && definition.getCode().asDexCode().getDebugInfo()
                     == DexDebugInfoForSingleLineMethod.getInstance()) {
               assert firstPosition.originalLine == lastPosition.originalLine;
               obfuscatedRange = new Range(0, MAX_LINE_NUMBER);
@@ -745,11 +747,11 @@
             }
             i = j;
           }
-          if (method.getCode().isDexCode()
-              && method.getCode().asDexCode().getDebugInfo()
+          if (definition.getCode().isDexCode()
+              && definition.getCode().asDexCode().getDebugInfo()
                   == DexDebugInfoForSingleLineMethod.getInstance()) {
             pcBasedDebugInfo.recordSingleLineFor(
-                method.getCode().asDexCode(), method.getParameters().size());
+                definition.getCode().asDexCode(), method.getParameters().size());
           }
         } // for each method of the group
       } // for each method group, grouped by name
@@ -815,7 +817,7 @@
   }
 
   private static boolean verifyMethodsAreKeptDirectlyOrIndirectly(
-      AppView<?> appView, List<DexEncodedMethod> methods) {
+      AppView<?> appView, List<ProgramMethod> methods) {
     if (appView.options().isGeneratingClassFiles() || !appView.appInfo().hasClassHierarchy()) {
       return true;
     }
@@ -823,9 +825,9 @@
     KeepInfoCollection keepInfo = appView.getKeepInfo();
     boolean allSeenAreInstanceInitializers = true;
     DexString originalName = null;
-    for (DexEncodedMethod method : methods) {
+    for (ProgramMethod method : methods) {
       // We cannot rename instance initializers.
-      if (method.isInstanceInitializer()) {
+      if (method.getDefinition().isInstanceInitializer()) {
         assert allSeenAreInstanceInitializers;
         continue;
       }
@@ -835,7 +837,7 @@
         continue;
       }
       // With desugared library, call-backs names are reserved here.
-      if (method.isLibraryMethodOverride().isTrue()) {
+      if (method.getDefinition().isLibraryMethodOverride().isTrue()) {
         continue;
       }
       // We use the same name for interface names even if it has different types.
@@ -856,8 +858,8 @@
     return true;
   }
 
-  private static int getMethodStartLine(DexEncodedMethod method) {
-    Code code = method.getCode();
+  private static int getMethodStartLine(ProgramMethod method) {
+    Code code = method.getDefinition().getCode();
     if (code == null) {
       return 0;
     }
@@ -878,14 +880,14 @@
 
   // Sort by startline, then DexEncodedMethod.slowCompare.
   // Use startLine = 0 if no debuginfo.
-  private static void sortMethods(List<DexEncodedMethod> methods) {
+  private static void sortMethods(List<ProgramMethod> methods) {
     methods.sort(
         (lhs, rhs) -> {
           int lhsStartLine = getMethodStartLine(lhs);
           int rhsStartLine = getMethodStartLine(rhs);
           int startLineDiff = lhsStartLine - rhsStartLine;
           if (startLineDiff != 0) return startLineDiff;
-          return DexEncodedMethod.slowCompare(lhs, rhs);
+          return DexEncodedMethod.slowCompare(lhs.getDefinition(), rhs.getDefinition());
         });
   }
 
@@ -921,21 +923,22 @@
         });
   }
 
-  public static IdentityHashMap<DexString, List<DexEncodedMethod>> groupMethodsByRenamedName(
+  public static IdentityHashMap<DexString, List<ProgramMethod>> groupMethodsByRenamedName(
       GraphLens graphLens, NamingLens namingLens, DexProgramClass clazz) {
-    IdentityHashMap<DexString, List<DexEncodedMethod>> methodsByRenamedName =
+    IdentityHashMap<DexString, List<ProgramMethod>> methodsByRenamedName =
         new IdentityHashMap<>(clazz.getMethodCollection().size());
-    for (DexEncodedMethod encodedMethod : clazz.methods()) {
+    for (ProgramMethod programMethod : clazz.programMethods()) {
       // Add method only if renamed, moved, or contains positions.
-      DexMethod method = encodedMethod.getReference();
+      DexEncodedMethod definition = programMethod.getDefinition();
+      DexMethod method = programMethod.getReference();
       DexString renamedName = namingLens.lookupName(method);
       if (renamedName != method.name
           || graphLens.getOriginalMethodSignature(method) != method
-          || doesContainPositions(encodedMethod)
-          || encodedMethod.isD8R8Synthesized()) {
+          || doesContainPositions(definition)
+          || definition.isD8R8Synthesized()) {
         methodsByRenamedName
             .computeIfAbsent(renamedName, key -> new ArrayList<>())
-            .add(encodedMethod);
+            .add(programMethod);
       }
     }
     return methodsByRenamedName;
@@ -1206,10 +1209,10 @@
   }
 
   private static List<MappedPosition> optimizeCfCodePositions(
-      DexEncodedMethod method, PositionRemapper positionRemapper, AppView<?> appView) {
+      ProgramMethod method, PositionRemapper positionRemapper, AppView<?> appView) {
     List<MappedPosition> mappedPositions = new ArrayList<>();
     // Do the actual processing for each method.
-    CfCode oldCode = method.getCode().asCfCode();
+    CfCode oldCode = method.getDefinition().getCode().asCfCode();
     List<CfInstruction> oldInstructions = oldCode.getInstructions();
     List<CfInstruction> newInstructions = new ArrayList<>(oldInstructions.size());
     for (CfInstruction oldInstruction : oldInstructions) {
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 328f9ff..8b89088 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.ir;
 
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -14,7 +13,6 @@
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
 import com.android.tools.r8.smali.SmaliTestBase;
 import com.android.tools.r8.utils.AndroidApp;
@@ -26,7 +24,6 @@
 import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
-import org.antlr.runtime.RecognitionException;
 
 public class IrInjectionTestBase extends SmaliTestBase {
 
@@ -105,7 +102,7 @@
     public String run() throws IOException {
       Timing timing = Timing.empty();
       IRConverter converter = new IRConverter(appView, timing, null);
-      converter.replaceCodeForTesting(method, code);
+      converter.replaceCodeForTesting(code);
       AndroidApp app = writeDex();
       return runOnArtRaw(app, DEFAULT_MAIN_CLASS_NAME).stdout;
     }
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index 1662269..a376a2e 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -87,6 +87,7 @@
     // Try split between all non-argument instructions in the first block.
     for (int i = argumentInstructions; i < firstBlockInstructions; i++) {
       TestApplication test = codeWithoutCatchHandlers();
+      AppView<?> appView = test.appView;
       IRCode code = test.code;
       assertEquals(initialBlockCount, code.blocks.size());
 
@@ -100,7 +101,7 @@
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code);
-      assertTrue(code.isConsistentSSA());
+      assertTrue(code.isConsistentSSA(appView));
 
       assertEquals(initialBlockCount + 1, code.blocks.size());
       assertEquals(i + 1, code.entryBlock().getInstructions().size());
@@ -121,6 +122,7 @@
     // Try split out all non-argument instructions in the first block.
     for (int i = argumentInstructions; i < firstBlockInstructions - 1; i++) {
       TestApplication test = codeWithoutCatchHandlers();
+      AppView<?> appView = test.appView;
       IRCode code = test.code;
       assertEquals(initialBlockCount, code.blocks.size());
 
@@ -134,7 +136,7 @@
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code, 1);
-      assertTrue(code.isConsistentSSA());
+      assertTrue(code.isConsistentSSA(appView));
 
       assertEquals(initialBlockCount + 2, code.blocks.size());
       assertEquals(i + 1, code.entryBlock().getInstructions().size());
@@ -208,6 +210,7 @@
     // Try split between all instructions in second block.
     for (int i = 1; i < secondBlockInstructions; i++) {
       TestApplication test = codeWithCatchHandlers(codeThrows, twoGuards);
+      AppView<?> appView = test.appView;
       IRCode code = test.code;
       assertEquals(initialBlockCount, code.blocks.size());
 
@@ -217,7 +220,7 @@
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code);
-      assertTrue(code.isConsistentSSA());
+      assertTrue(code.isConsistentSSA(appView));
 
       assertEquals(initialBlockCount + 1, code.blocks.size());
       assertEquals(i + 1, code.blocks.get(1).getInstructions().size());
@@ -247,6 +250,7 @@
     // Try split out all instructions in second block.
     for (int i = 1; i < secondBlockInstructions - 1; i++) {
       TestApplication test = codeWithCatchHandlers(codeThrows, twoGuards);
+      AppView<?> appView = test.appView;
       IRCode code = test.code;
       assertEquals(initialBlockCount, code.blocks.size());
 
@@ -256,7 +260,7 @@
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code, 1);
-      assertTrue(code.isConsistentSSA());
+      assertTrue(code.isConsistentSSA(appView));
 
       assertEquals(initialBlockCount + 2, code.blocks.size());
       assertEquals(i + 1, code.blocks.get(1).getInstructions().size());
@@ -323,6 +327,7 @@
     // Try split between all non-argument instructions in the first block.
     for (int i = argumentInstructions; i < firstBlockInstructions; i++) {
       TestApplication test = codeWithIf(hitTrueBranch);
+      AppView<?> appView = test.appView;
       IRCode code = test.code;
       assertEquals(initialBlockCount, code.blocks.size());
 
@@ -336,7 +341,7 @@
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code);
-      assertTrue(code.isConsistentSSA());
+      assertTrue(code.isConsistentSSA(appView));
 
       assertEquals(initialBlockCount + 1, code.blocks.size());
       assertEquals(i + 1, code.entryBlock().getInstructions().size());
@@ -444,6 +449,7 @@
     // Try split between all non-argument instructions in the first block.
     for (int i = argumentInstructions; i < firstBlockInstructions; i++) {
       TestApplication test = codeWithSwitch(hitCase);
+      AppView<?> appView = test.appView;
       IRCode code = test.code;
       assertEquals(initialBlockCount, code.blocks.size());
 
@@ -457,7 +463,7 @@
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code);
-      assertTrue(code.isConsistentSSA());
+      assertTrue(code.isConsistentSSA(appView));
 
       assertEquals(initialBlockCount + 1, code.blocks.size());
       assertEquals(i + 1, code.entryBlock().getInstructions().size());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
index 198139e..1427b24 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
@@ -148,7 +148,7 @@
             IRMetadata.unknown(),
             Origin.unknown(),
             new MutableMethodConversionOptions(options));
-    PeepholeOptimizer.optimize(code, new MockLinearScanRegisterAllocator(appView, code));
+    PeepholeOptimizer.optimize(appView, code, new MockLinearScanRegisterAllocator(appView, code));
 
     // Check that all four constant number instructions remain.
     assertEquals(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index 19ff50d..9d6d411 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -65,7 +65,7 @@
     AssumeInserter assumeInserter = new AssumeInserter(appView);
 
     assumeInserter.insertAssumeInstructions(code, Timing.empty());
-    assertTrue(code.isConsistentSSA());
+    assertTrue(code.isConsistentSSA(appView));
     checkCountOfNonNull(code, expectedNumberOfNonNull);
 
     if (testAugmentedIRCode != null) {
@@ -73,7 +73,7 @@
     }
 
     CodeRewriter.removeAssumeInstructions(appView, code);
-    assertTrue(code.isConsistentSSA());
+    assertTrue(code.isConsistentSSA(appView));
     checkCountOfNonNull(code, 0);
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
index c0cc06d..7bbc000 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -54,7 +54,9 @@
   private final IRMetadata metadata = IRMetadata.unknown();
 
   @Test
-  public void trivialGotoInEntryBlock() {
+  public void trivialGotoInEntryBlock() throws Exception {
+    AppView<AppInfo> appView = computeAppView(AndroidApp.builder().build());
+    InternalOptions options = appView.options();
     // Setup silly block structure:
     //
     // block0:
@@ -93,7 +95,6 @@
     // Check that the goto in block0 remains. There was a bug in the trivial goto elimination
     // that ended up removing that goto changing the code to start with the unreachable
     // throw.
-    InternalOptions options = new InternalOptions();
     options.debug = true;
     IRCode code =
         new IRCode(
@@ -105,7 +106,7 @@
             IRMetadata.unknown(),
             Origin.unknown(),
             new MutableMethodConversionOptions(options));
-    CodeRewriter.collapseTrivialGotos(code);
+    CodeRewriter.collapseTrivialGotos(appView, code);
     assertTrue(code.entryBlock().isTrivialGoto());
     assertTrue(blocks.contains(block0));
     assertTrue(blocks.contains(block1));
@@ -193,7 +194,7 @@
             IRMetadata.unknown(),
             Origin.unknown(),
             new MutableMethodConversionOptions(options));
-    CodeRewriter.collapseTrivialGotos(code);
+    CodeRewriter.collapseTrivialGotos(appView, code);
     assertTrue(block0.getInstructions().get(1).isIf());
     assertEquals(block1, block0.getInstructions().get(1).asIf().fallthroughBlock());
     assertTrue(blocks.containsAll(ImmutableList.of(block0, block1, block2, block3)));
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 8e7929e..dd40ecf 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -877,7 +877,7 @@
                 Origin.unknown(),
                 new MutableMethodConversionOptions(options));
         RegisterAllocator allocator = new LinearScanRegisterAllocator(appView, ir);
-        method.setCode(
+        programMethod.setCode(
             new DexBuilder(ir, BytecodeMetadataProvider.empty(), allocator, options).build(),
             appView);
         directMethods[i] = method;