Ensure all conversions are split into supported LIR phases.

Bug: b/225838009
Bug: b/288227719
Change-Id: I097646ec352cff13da3c583628ae4416e3a44377
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 90faadc..3fc29d6 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.contexts.CompilationContext;
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
@@ -190,7 +191,8 @@
                 method,
                 OptimizationFeedbackIgnore.getInstance(),
                 methodProcessor,
-                methodProcessingContext));
+                methodProcessingContext,
+                MethodConversionOptions.forD8(converter.appView)));
   }
 
   private void writeAnnotations(
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 b7feac3..e2a42f7 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -36,8 +36,8 @@
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
@@ -578,7 +578,7 @@
         callerPosition,
         origin,
         protoChanges,
-        new ThrowingMethodConversionOptions(appView.options()));
+        MethodConversionOptions.nonConverting());
   }
 
   private void verifyFramesOrRemove(ProgramMethod method, AppView<?> appView, GraphLens codeLens) {
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 193e758..3f04412 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.PositionBuilder;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.lightir.LirCode;
 import com.android.tools.r8.origin.Origin;
@@ -24,7 +25,7 @@
 public abstract class Code extends CachedHashValueDexItem {
 
   public final IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
-    return buildIR(method, appView, origin, new MutableMethodConversionOptions(appView.options()));
+    return buildIR(method, appView, origin, MethodConversionOptions.forLirPhase(appView));
   }
 
   public abstract IRCode buildIR(
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 31b2efe..4adaae6 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -26,8 +26,8 @@
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.SyntheticStraightLineSourceCode;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
@@ -166,7 +166,7 @@
         new DefaultInstanceInitializerSourceCode(originalMethod, callerPosition);
     return IRBuilder.createForInlining(
             method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges)
-        .build(context, new ThrowingMethodConversionOptions(appView.options()));
+        .build(context, MethodConversionOptions.nonConverting());
   }
 
   @Override
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 325eb79..f59c187 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -36,8 +36,8 @@
 import com.android.tools.r8.ir.conversion.DexSourceCode;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.DexDebugUtils.PositionInfo;
@@ -554,7 +554,7 @@
             appView.dexItemFactory());
     return IRBuilder.createForInlining(
             method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges)
-        .build(context, new ThrowingMethodConversionOptions(appView.options()));
+        .build(context, MethodConversionOptions.nonConverting());
   }
 
   @Override
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 ff6d789..32ba2c2 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -29,7 +30,7 @@
   }
 
   public IRCode buildIR(AppView<?> appView) {
-    return buildIR(appView, new MutableMethodConversionOptions(appView.options()));
+    return buildIR(appView, MethodConversionOptions.forLirPhase(appView));
   }
 
   public IRCode buildIR(AppView<?> appView, MutableMethodConversionOptions conversionOptions) {
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
index 90c6d8d..735be28 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -20,8 +20,8 @@
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.SyntheticStraightLineSourceCode;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
@@ -77,7 +77,7 @@
     ThrowNullSourceCode source = new ThrowNullSourceCode(method, callerPosition);
     return IRBuilder.createForInlining(
             method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges)
-        .build(context, new ThrowingMethodConversionOptions(appView.options()));
+        .build(context, MethodConversionOptions.nonConverting());
   }
 
   @Override
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 9f2e2ad..59efae5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -17,6 +17,8 @@
 import com.android.tools.r8.graph.lens.MethodLookupResult;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
 import com.android.tools.r8.ir.code.InvokeType;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -88,7 +90,7 @@
       timing.begin("HorizontalClassMerger (" + mode.toString() + ")");
       IRCodeProvider codeProvider =
           appView.hasClassHierarchy()
-              ? IRCodeProvider.create(appView.withClassHierarchy())
+              ? IRCodeProvider.create(appView.withClassHierarchy(), this::getConversionOptions)
               : IRCodeProvider.createThrowing();
       run(runtimeTypeCheckInfo, codeProvider, executorService, timing);
 
@@ -104,6 +106,12 @@
     }
   }
 
+  private MutableMethodConversionOptions getConversionOptions() {
+    return mode == Mode.INITIAL
+        ? MethodConversionOptions.forPreLirPhase(appView)
+        : MethodConversionOptions.forPostLirPhase(appView);
+  }
+
   private void run(
       RuntimeTypeCheckInfo runtimeTypeCheckInfo,
       IRCodeProvider codeProvider,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
index ab14412..6a35c88 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
@@ -10,7 +10,9 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
+import java.util.function.Supplier;
 
 public interface IRCodeProvider {
 
@@ -18,8 +20,10 @@
 
   void setGraphLens(GraphLens graphLens);
 
-  static IRCodeProvider create(AppView<? extends AppInfoWithClassHierarchy> appView) {
-    return new IRCodeProviderImpl(appView);
+  static IRCodeProvider create(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      Supplier<MutableMethodConversionOptions> getConversionOptions) {
+    return new IRCodeProviderImpl(appView, getConversionOptions);
   }
 
   static IRCodeProvider createThrowing() {
@@ -37,8 +41,11 @@
   class IRCodeProviderImpl implements IRCodeProvider {
 
     private final AppView<AppInfo> appViewForConversion;
+    private Supplier<MutableMethodConversionOptions> getConversionOptions;
 
-    private IRCodeProviderImpl(AppView<? extends AppInfoWithClassHierarchy> appView) {
+    private IRCodeProviderImpl(
+        AppView<? extends AppInfoWithClassHierarchy> appView,
+        Supplier<MutableMethodConversionOptions> getConversionOptions) {
       // At this point the code rewritings described by repackaging and synthetic finalization have
       // not been applied to the code objects. These code rewritings will be applied in the
       // application writer. We therefore simulate that we are in D8, to allow building IR for each
@@ -51,6 +58,7 @@
       appViewForConversion.setGraphLens(appView.graphLens());
       appViewForConversion.setCodeLens(appView.codeLens());
       this.appViewForConversion = appViewForConversion;
+      this.getConversionOptions = getConversionOptions;
     }
 
     @Override
@@ -58,7 +66,7 @@
       return method
           .getDefinition()
           .getCode()
-          .buildIR(method, appViewForConversion, method.getOrigin());
+          .buildIR(method, appViewForConversion, method.getOrigin(), getConversionOptions.get());
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index a78dc55..613d763 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
@@ -178,7 +179,8 @@
                 method,
                 OptimizationFeedbackIgnore.getInstance(),
                 methodProcessor,
-                methodProcessingContext),
+                methodProcessingContext,
+                MethodConversionOptions.forPostLirPhase(appView)),
         executorService);
     timing.end();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index 857ea01..f95d148 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.conversion.callgraph.Node;
 import com.android.tools.r8.ir.optimize.Inliner;
@@ -229,7 +230,7 @@
       DexProgramClass builder,
       ProgramMethod dynamicMethod,
       IRConverter converter) {
-    IRCode code = dynamicMethod.buildIR(appView);
+    IRCode code = dynamicMethod.buildIR(appView, MethodConversionOptions.forPreLirPhase(appView));
     InstructionListIterator instructionIterator = code.instructionListIterator();
 
     assert builder.superType == references.generatedMessageLiteBuilderType
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 809f0ad..608c06f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -41,6 +41,7 @@
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
@@ -258,7 +259,8 @@
                 method,
                 OptimizationFeedbackIgnore.getInstance(),
                 methodProcessor,
-                methodProcessingContext),
+                methodProcessingContext,
+                MethodConversionOptions.forPostLirPhase(appView)),
         executorService);
     timing.end();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 61747c0..8e43229 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -35,6 +35,7 @@
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
@@ -167,7 +168,7 @@
     DexType holder = dynamicMethod.getHolderType();
     assert !protos.containsKey(holder);
 
-    IRCode code = dynamicMethod.buildIR(appView);
+    IRCode code = dynamicMethod.buildIR(appView, MethodConversionOptions.nonConverting());
     InvokeMethod newMessageInfoInvoke =
         GeneratedMessageLiteShrinker.getNewMessageInfoInvoke(code, references);
     ProtoMessageInfo protoMessageInfo =
@@ -231,7 +232,7 @@
                 return;
               }
 
-              IRCode code = clinit.buildIR(appView);
+              IRCode code = clinit.buildIR(appView, MethodConversionOptions.nonConverting());
               Map<DexEncodedField, StaticPut> uniqueStaticPuts =
                   IRCodeUtils.findUniqueStaticPuts(appView, code, extensionFields);
               for (DexEncodedField extensionField : extensionFields) {
@@ -256,7 +257,8 @@
   private Map<DexProgramClass, Set<DexEncodedField>> collectExtensionFields() {
     Map<DexProgramClass, Set<DexEncodedField>> extensionFieldsByClass = new IdentityHashMap<>();
     for (ProgramMethod findLiteExtensionByNumberMethod : findLiteExtensionByNumberMethods) {
-      IRCode code = findLiteExtensionByNumberMethod.buildIR(appView);
+      IRCode code =
+          findLiteExtensionByNumberMethod.buildIR(appView, MethodConversionOptions.nonConverting());
       Set<Phi> seenPhis = Sets.newIdentityHashSet();
       for (BasicBlock block : code.blocks(BasicBlock::isReturnBlock)) {
         Value returnValue = block.exit().asReturn().returnValue();
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 ae9434b..73836d1 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
@@ -87,7 +87,7 @@
     InternalOptions options = appView.options();
     if (options.debug
         || code.context().getOrComputeReachabilitySensitive(appView)
-        || code.getConversionOptions().isGeneratingClassFiles()) {
+        || !code.getConversionOptions().isGeneratingDex()) {
       return DeadInstructionResult.notDead();
     }
     return DeadInstructionResult.deadIfOutValueIsDead();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index 6c7a613..4e778ae 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -128,7 +128,8 @@
                     method,
                     OptimizationFeedbackIgnore.getInstance(),
                     this,
-                    processorContext.createMethodProcessingContext(method)),
+                    processorContext.createMethodProcessingContext(method),
+                    MethodConversionOptions.forD8(converter.appView)),
             executorService));
   }
 
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 7e0cf3a..6aa94c2 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
@@ -29,6 +29,7 @@
 import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.passes.ArrayConstructionSimplifier;
 import com.android.tools.r8.ir.conversion.passes.BinopRewriter;
 import com.android.tools.r8.ir.conversion.passes.BranchSimplifier;
@@ -413,15 +414,18 @@
   public void optimizeSynthesizedMethods(
       List<ProgramMethod> programMethods,
       MethodProcessorEventConsumer eventConsumer,
+      MutableMethodConversionOptions conversionOptions,
       ExecutorService executorService)
       throws ExecutionException {
     // Process the generated class, but don't apply any outlining.
     ProgramMethodSet methods = ProgramMethodSet.create(programMethods::forEach);
-    processMethodsConcurrently(methods, eventConsumer, executorService);
+    processMethodsConcurrently(methods, eventConsumer, conversionOptions, executorService);
   }
 
   public void optimizeSynthesizedMethod(
-      ProgramMethod synthesizedMethod, MethodProcessorEventConsumer eventConsumer) {
+      ProgramMethod synthesizedMethod,
+      MethodProcessorEventConsumer eventConsumer,
+      MutableMethodConversionOptions conversionOptions) {
     if (!synthesizedMethod.getDefinition().isProcessed()) {
       // Process the generated method, but don't apply any outlining.
       OneTimeMethodProcessor methodProcessor =
@@ -429,25 +433,31 @@
       methodProcessor.forEachWaveWithExtension(
           (method, methodProcessingContext) ->
               processDesugaredMethod(
-                  method, delayedOptimizationFeedback, methodProcessor, methodProcessingContext));
+                  method,
+                  delayedOptimizationFeedback,
+                  methodProcessor,
+                  methodProcessingContext,
+                  conversionOptions));
     }
   }
 
   public void processClassesConcurrently(
       Collection<DexProgramClass> classes,
       MethodProcessorEventConsumer eventConsumer,
+      MutableMethodConversionOptions conversionOptions,
       ExecutorService executorService)
       throws ExecutionException {
     ProgramMethodSet wave = ProgramMethodSet.create();
     for (DexProgramClass clazz : classes) {
       clazz.forEachProgramMethod(wave::add);
     }
-    processMethodsConcurrently(wave, eventConsumer, executorService);
+    processMethodsConcurrently(wave, eventConsumer, conversionOptions, executorService);
   }
 
   public void processMethodsConcurrently(
       ProgramMethodSet wave,
       MethodProcessorEventConsumer eventConsumer,
+      MutableMethodConversionOptions conversionOptions,
       ExecutorService executorService)
       throws ExecutionException {
     if (!wave.isEmpty()) {
@@ -456,7 +466,11 @@
       methodProcessor.forEachWaveWithExtension(
           (method, methodProcessingContext) ->
               processDesugaredMethod(
-                  method, delayedOptimizationFeedback, methodProcessor, methodProcessingContext),
+                  method,
+                  delayedOptimizationFeedback,
+                  methodProcessor,
+                  methodProcessingContext,
+                  conversionOptions),
           executorService);
     }
   }
@@ -466,12 +480,14 @@
       ProgramMethod method,
       OptimizationFeedback feedback,
       MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext) {
+      MethodProcessingContext methodProcessingContext,
+      MutableMethodConversionOptions conversionOptions) {
     DexEncodedMethod definition = method.getDefinition();
     Code code = definition.getCode();
     boolean matchesMethodFilter = options.methodMatchesFilter(definition);
     if (code != null && matchesMethodFilter) {
-      return rewriteDesugaredCode(method, feedback, methodProcessor, methodProcessingContext);
+      return rewriteDesugaredCode(
+          method, feedback, methodProcessor, methodProcessingContext, conversionOptions);
     } else {
       // Mark abstract methods as processed as well.
       definition.markProcessed(ConstraintWithTarget.NEVER);
@@ -491,20 +507,22 @@
       ProgramMethod method,
       OptimizationFeedback feedback,
       MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext) {
+      MethodProcessingContext methodProcessingContext,
+      MutableMethodConversionOptions conversionOptions) {
     return ExceptionUtils.withOriginAndPositionAttachmentHandler(
         method.getOrigin(),
         new MethodPosition(method.getReference().asMethodReference()),
         () ->
             rewriteDesugaredCodeInternal(
-                method, feedback, methodProcessor, methodProcessingContext));
+                method, feedback, methodProcessor, methodProcessingContext, conversionOptions));
   }
 
   protected Timing rewriteDesugaredCodeInternal(
       ProgramMethod method,
       OptimizationFeedback feedback,
       MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext) {
+      MethodProcessingContext methodProcessingContext,
+      MutableMethodConversionOptions conversionOptions) {
     if (options.verbose) {
       options.reporter.info(
           new StringDiagnostic("Processing: " + method.toSourceString()));
@@ -518,7 +536,7 @@
       return Timing.empty();
     }
 
-    IRCode code = method.buildIR(appView);
+    IRCode code = method.buildIR(appView, conversionOptions);
     if (code == null) {
       feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER);
       return Timing.empty();
@@ -1068,16 +1086,17 @@
     if (options.testing.roundtripThroughLir) {
       code = roundtripThroughLir(code, feedback, bytecodeMetadataProvider, timing);
     }
-    if (options.testing.canUseLir(appView)) {
+    MethodConversionOptions conversionOptions = code.getConversionOptions();
+    if (conversionOptions.isGeneratingLir()) {
       timing.begin("IR->LIR");
       finalizeToLir(code, feedback, bytecodeMetadataProvider, timing);
       timing.end();
-    } else if (options.isGeneratingClassFiles()) {
+    } else if (conversionOptions.isGeneratingClassFiles()) {
       timing.begin("IR->CF");
       finalizeToCf(code, feedback, bytecodeMetadataProvider, timing);
       timing.end();
     } else {
-      assert options.isGeneratingDex();
+      assert conversionOptions.isGeneratingDex();
       timing.begin("IR->DEX");
       finalizeToDex(code, feedback, bytecodeMetadataProvider, timing);
       timing.end();
@@ -1130,7 +1149,8 @@
             appView,
             null,
             RewrittenPrototypeDescription.none(),
-            appView.graphLens().getOriginalMethodSignature(code.context().getReference()));
+            appView.graphLens().getOriginalMethodSignature(code.context().getReference()),
+            (MutableMethodConversionOptions) code.getConversionOptions());
     timing.end();
     return irCode;
   }
@@ -1289,32 +1309,4 @@
       inliner.onMethodCodePruned(method);
     }
   }
-
-  public void finalizeLirMethodToOutputFormat(ProgramMethod method) {
-    Code code = method.getDefinition().getCode();
-    if (!(code instanceof LirCode)) {
-      return;
-    }
-    Timing onThreadTiming = Timing.empty();
-    IRCode irCode = method.buildIR(appView);
-    // Processing is done and no further uses of the meta-data should arise.
-    BytecodeMetadataProvider bytecodeMetadataProvider = BytecodeMetadataProvider.empty();
-    // During processing optimization info may cause previously live code to become dead.
-    // E.g., we may now have knowledge that an invoke does not have side effects.
-    // Thus, we re-run the dead-code remover now as it is assumed complete by CF/DEX finalization.
-    deadCodeRemover.run(irCode, onThreadTiming);
-    if (options.isGeneratingClassFiles()) {
-      method.setCode(
-          new IRToCfFinalizer(appView, deadCodeRemover)
-              .finalizeCode(irCode, bytecodeMetadataProvider, onThreadTiming),
-          appView);
-    } else {
-      assert options.isGeneratingDex();
-      method.setCode(
-          new IRToDexFinalizer(appView, deadCodeRemover)
-              .finalizeCode(irCode, bytecodeMetadataProvider, onThreadTiming),
-          appView);
-      updateHighestSortingStrings(method.getDefinition());
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
index adf0102..69c916e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
@@ -5,15 +5,67 @@
 package com.android.tools.r8.ir.conversion;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.utils.InternalOptions;
 
 public abstract class MethodConversionOptions {
 
+  public static MutableMethodConversionOptions forPreLirPhase(AppView<?> appView) {
+    if (!appView.enableWholeProgramOptimizations()) {
+      return forD8(appView);
+    }
+    assert appView.testing().isPreLirPhase();
+    return new MutableMethodConversionOptions(Target.CF, appView.options());
+  }
+
+  public static MutableMethodConversionOptions forPostLirPhase(AppView<?> appView) {
+    if (!appView.enableWholeProgramOptimizations()) {
+      return forD8(appView);
+    }
+    assert appView.testing().isPostLirPhase();
+    Target target = appView.options().isGeneratingClassFiles() ? Target.CF : Target.DEX;
+    return new MutableMethodConversionOptions(target, appView.options());
+  }
+
+  public static MutableMethodConversionOptions forLirPhase(AppView<?> appView) {
+    if (!appView.enableWholeProgramOptimizations()) {
+      return forD8(appView);
+    }
+    assert appView.testing().isSupportedLirPhase();
+    return new MutableMethodConversionOptions(determineTarget(appView), appView.options());
+  }
+
+  public static MutableMethodConversionOptions forD8(AppView<?> appView) {
+    assert !appView.enableWholeProgramOptimizations();
+    return new MutableMethodConversionOptions(determineTarget(appView), appView.options());
+  }
+
+  public static MutableMethodConversionOptions nonConverting() {
+    return new ThrowingMethodConversionOptions();
+  }
+
+  private enum Target {
+    CF,
+    DEX,
+    LIR
+  }
+
+  private static Target determineTarget(AppView<?> appView) {
+    if (appView.testing().canUseLir(appView)) {
+      return Target.LIR;
+    }
+    if (appView.options().isGeneratingClassFiles()) {
+      return Target.CF;
+    }
+    assert appView.options().isGeneratingDex();
+    return Target.DEX;
+  }
+
+  public abstract boolean isGeneratingLir();
+
   public abstract boolean isGeneratingClassFiles();
 
-  public final boolean isGeneratingDex() {
-    return !isGeneratingClassFiles();
-  }
+  public abstract boolean isGeneratingDex();
 
   public abstract boolean isPeepholeOptimizationsEnabled();
 
@@ -21,13 +73,17 @@
 
   public static class MutableMethodConversionOptions extends MethodConversionOptions {
 
+    private Target target;
     private boolean enablePeepholeOptimizations = true;
     private boolean enableStringSwitchConversion;
-    private boolean isGeneratingClassFiles;
 
-    public MutableMethodConversionOptions(InternalOptions options) {
-      this.enableStringSwitchConversion = options.isStringSwitchConversionEnabled();
-      this.isGeneratingClassFiles = options.isGeneratingClassFiles();
+    private MutableMethodConversionOptions(Target target, boolean enableStringSwitchConversion) {
+      this.target = target;
+      this.enableStringSwitchConversion = enableStringSwitchConversion;
+    }
+
+    private MutableMethodConversionOptions(Target target, InternalOptions options) {
+      this(target, options.isStringSwitchConversionEnabled());
     }
 
     public void disablePeepholeOptimizations(MethodProcessor methodProcessor) {
@@ -40,15 +96,19 @@
       return this;
     }
 
-    public MutableMethodConversionOptions setIsGeneratingClassFiles(
-        boolean isGeneratingClassFiles) {
-      this.isGeneratingClassFiles = isGeneratingClassFiles;
-      return this;
+    @Override
+    public boolean isGeneratingLir() {
+      return target == Target.LIR;
     }
 
     @Override
     public boolean isGeneratingClassFiles() {
-      return isGeneratingClassFiles;
+      return target == Target.CF;
+    }
+
+    @Override
+    public boolean isGeneratingDex() {
+      return target == Target.DEX;
     }
 
     @Override
@@ -64,8 +124,13 @@
 
   public static class ThrowingMethodConversionOptions extends MutableMethodConversionOptions {
 
-    public ThrowingMethodConversionOptions(InternalOptions options) {
-      super(options);
+    private ThrowingMethodConversionOptions() {
+      super(null, true);
+    }
+
+    @Override
+    public boolean isGeneratingLir() {
+      throw new Unreachable();
     }
 
     @Override
@@ -74,6 +139,11 @@
     }
 
     @Override
+    public boolean isGeneratingDex() {
+      throw new Unreachable();
+    }
+
+    @Override
     public boolean isPeepholeOptimizationsEnabled() {
       throw new Unreachable();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index bcbbe63..bcadf66 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -380,7 +380,12 @@
       MethodProcessor methodProcessor,
       MethodProcessingContext methodProcessingContext) {
     boolean didDesugar = desugar(method, desugaringEventConsumer, methodProcessingContext);
-    return rewriteDesugaredCodeInternal(method, feedback, methodProcessor, methodProcessingContext);
+    return rewriteDesugaredCodeInternal(
+        method,
+        feedback,
+        methodProcessor,
+        methodProcessingContext,
+        MethodConversionOptions.forD8(appView));
   }
 
   private boolean desugar(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index 9df87ae..bfdb3f4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -6,14 +6,19 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.analysis.fieldaccess.TrivialFieldAccessReprocessor;
+import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
+import com.android.tools.r8.lightir.LirCode;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -51,6 +56,7 @@
   private DexApplication internalOptimize(
       AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
       throws ExecutionException {
+    appView.testing().enterLirSupportedPhase();
     // Desugaring happens in the enqueuer.
     assert instructionDesugaring.isEmpty();
 
@@ -93,7 +99,11 @@
       primaryMethodProcessor.forEachMethod(
           (method, methodProcessingContext) ->
               processDesugaredMethod(
-                  method, feedback, primaryMethodProcessor, methodProcessingContext),
+                  method,
+                  feedback,
+                  primaryMethodProcessor,
+                  methodProcessingContext,
+                  MethodConversionOptions.forLirPhase(appView)),
           this::waveStart,
           this::waveDone,
           timing,
@@ -168,7 +178,11 @@
         postMethodProcessor.forEachMethod(
             (method, methodProcessingContext) ->
                 processDesugaredMethod(
-                    method, feedback, postMethodProcessor, methodProcessingContext),
+                    method,
+                    feedback,
+                    postMethodProcessor,
+                    methodProcessingContext,
+                    MethodConversionOptions.forLirPhase(appView)),
             feedback,
             executorService,
             timing);
@@ -214,6 +228,7 @@
 
   private void finalizeLirToOutputFormat(Timing timing, ExecutorService executorService)
       throws ExecutionException {
+    appView.testing().exitLirSupportedPhase();
     if (!options.testing.canUseLir(appView)) {
       return;
     }
@@ -230,6 +245,35 @@
     timing.end();
   }
 
+  void finalizeLirMethodToOutputFormat(ProgramMethod method) {
+    Code code = method.getDefinition().getCode();
+    if (!(code instanceof LirCode)) {
+      return;
+    }
+    Timing onThreadTiming = Timing.empty();
+    IRCode irCode = method.buildIR(appView, MethodConversionOptions.forPostLirPhase(appView));
+    // Processing is done and no further uses of the meta-data should arise.
+    BytecodeMetadataProvider bytecodeMetadataProvider = BytecodeMetadataProvider.empty();
+    // During processing optimization info may cause previously live code to become dead.
+    // E.g., we may now have knowledge that an invoke does not have side effects.
+    // Thus, we re-run the dead-code remover now as it is assumed complete by CF/DEX finalization.
+    deadCodeRemover.run(irCode, onThreadTiming);
+    MethodConversionOptions conversionOptions = irCode.getConversionOptions();
+    if (conversionOptions.isGeneratingClassFiles()) {
+      method.setCode(
+          new IRToCfFinalizer(appView, deadCodeRemover)
+              .finalizeCode(irCode, bytecodeMetadataProvider, onThreadTiming),
+          appView);
+    } else {
+      assert conversionOptions.isGeneratingDex();
+      method.setCode(
+          new IRToDexFinalizer(appView, deadCodeRemover)
+              .finalizeCode(irCode, bytecodeMetadataProvider, onThreadTiming),
+          appView);
+      updateHighestSortingStrings(method.getDefinition());
+    }
+  }
+
   private void clearDexMethodCompilationState() {
     appView.appInfo().classes().forEach(this::clearDexMethodCompilationState);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 8ac1b02..3fbcfee 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
 import com.google.common.base.Predicates;
@@ -198,7 +199,10 @@
             .build();
     // Optimize to generate DexCode instead of CfCode.
     ProgramMethod programMethod = new ProgramMethod(methodHolder, newVirtualMethod);
-    converter.optimizeSynthesizedMethod(programMethod, methodProcessorEventConsumer);
+    converter.optimizeSynthesizedMethod(
+        programMethod,
+        methodProcessorEventConsumer,
+        MethodConversionOptions.forD8(converter.appView));
     eventConsumer.acceptCovariantReturnTypeBridgeMethod(programMethod, method);
     return newVirtualMethod;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
index 6646ce0..bd8b0fc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.code.RecordFieldValues;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Timing;
@@ -67,7 +68,11 @@
         programMethod
             .getDefinition()
             .getCode()
-            .buildIR(programMethod, appView, programMethod.getOrigin());
+            .buildIR(
+                programMethod,
+                appView,
+                programMethod.getOrigin(),
+                MethodConversionOptions.forPostLirPhase(appView));
     boolean done = false;
     ListIterator<BasicBlock> blockIterator = irCode.listIterator();
     while (blockIterator.hasNext()) {
@@ -85,7 +90,6 @@
     assert done;
     irConverter.removeDeadCodeAndFinalizeIR(
         irCode, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
-    irConverter.finalizeLirMethodToOutputFormat(programMethod);
   }
 
   public void rewriteRecordFieldArray(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
index a40fca9..cdb30da 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -94,7 +95,10 @@
     List<DexEncodedField> switchMapFields = clazz.staticFields().stream()
         .filter(this::maybeIsSwitchMap).collect(Collectors.toList());
     if (!switchMapFields.isEmpty()) {
-      IRCode initializer = clazz.getProgramClassInitializer().buildIR(appView);
+      IRCode initializer =
+          clazz
+              .getProgramClassInitializer()
+              .buildIR(appView, MethodConversionOptions.nonConverting());
       switchMapFields.forEach(field -> extractSwitchMap(field, initializer));
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 115ab23..7f34299 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -319,9 +319,12 @@
         enumUnboxingCandidatesInfo.addMethodDependency(eligibleEnum, code.context());
       }
     }
-    if (methodsDependingOnLibraryModelisation.contains(code.context(), appView.graphLens())) {
-      code.mutateConversionOptions(
-          conversionOptions -> conversionOptions.disablePeepholeOptimizations(methodProcessor));
+    // TODO(b/225838009): Remove this when always using LIR.
+    if (!appView.testing().canUseLir(appView)) {
+      if (methodsDependingOnLibraryModelisation.contains(code.context(), appView.graphLens())) {
+        code.mutateConversionOptions(
+            conversionOptions -> conversionOptions.disablePeepholeOptimizations(methodProcessor));
+      }
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index eaa1d40..a0d4d36 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -50,6 +50,7 @@
 import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
@@ -283,7 +284,11 @@
     methodProcessor.forEachWaveWithExtension(
         (method, methodProcessingContext) ->
             converter.processDesugaredMethod(
-                method, OptimizationFeedback.getSimple(), methodProcessor, methodProcessingContext),
+                method,
+                OptimizationFeedback.getSimple(),
+                methodProcessor,
+                methodProcessingContext,
+                MethodConversionOptions.forLirPhase(appView)),
         executorService);
 
     return checkNotNullToCheckNotZeroMapping;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
index fc83f40..fe3de2f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
@@ -111,7 +112,8 @@
                   method,
                   OptimizationFeedbackSimple.getInstance(),
                   methodProcessor,
-                  methodProcessingContext),
+                  methodProcessingContext,
+                  MethodConversionOptions.forLirPhase(appView)),
           executorService);
       return utilityClasses;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
index 1cedcc3..45f03bc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
@@ -59,6 +59,7 @@
 import com.android.tools.r8.ir.code.ValueTypeConstraint;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.SourceCode;
@@ -1360,7 +1361,10 @@
       MethodProcessorEventConsumer methodProcessorEventConsumer =
           MethodProcessorEventConsumer.empty();
       converter.optimizeSynthesizedMethods(
-          outlineMethods, methodProcessorEventConsumer, executorService);
+          outlineMethods,
+          methodProcessorEventConsumer,
+          MethodConversionOptions.forLirPhase(appView),
+          executorService);
       feedback.updateVisibleOptimizationInfo();
       forEachSelectedOutliningMethod(
           converter,
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
index 2200050..f726dda 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
@@ -18,8 +18,8 @@
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.SourceCode;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.RetracerForCodePrinting;
@@ -68,7 +68,7 @@
             origin,
             valueNumberGenerator,
             protoChanges)
-        .build(context, new ThrowingMethodConversionOptions(appView.options()));
+        .build(context, MethodConversionOptions.nonConverting());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index f455dda..079b862 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -128,7 +128,8 @@
       AppView<?> appView,
       Position callerPosition,
       RewrittenPrototypeDescription protoChanges,
-      DexMethod originalMethod) {
+      DexMethod originalMethod,
+      MutableMethodConversionOptions conversionOptions) {
     Parser<EV> parser =
         new Parser<>(
             lirCode,
@@ -141,7 +142,7 @@
     parser.parseArguments(method);
     parser.ensureDebugInfo();
     lirCode.forEach(view -> view.accept(parser));
-    IRCode irCode = parser.getIRCode(method);
+    IRCode irCode = parser.getIRCode(method, conversionOptions);
     // Some instructions have bottom types (e.g., phis). Compute their actual types by widening.
     new TypeAnalysis(appView).widening(irCode);
     return irCode;
@@ -338,7 +339,8 @@
 
     // TODO(b/270398965): Replace LinkedList.
     @SuppressWarnings("JdkObsolete")
-    public IRCode getIRCode(ProgramMethod method) {
+    public IRCode getIRCode(
+        ProgramMethod method, MutableMethodConversionOptions conversionOptions) {
       LinkedList<BasicBlock> blockList = new LinkedList<>();
       IntList blockIndices = new IntArrayList(blocks.keySet());
       blockIndices.sort(Integer::compare);
@@ -364,7 +366,7 @@
           basicBlockNumberGenerator,
           code.getMetadataForIR(),
           method.getOrigin(),
-          new MutableMethodConversionOptions(appView.options()));
+          conversionOptions);
     }
 
     public BasicBlock getBasicBlock(int instructionIndex) {
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index d1a7149..0fccf4e 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.SourcePosition;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.InternalOptions;
@@ -282,7 +283,8 @@
       MutableMethodConversionOptions conversionOptions) {
     RewrittenPrototypeDescription protoChanges =
         appView.graphLens().lookupPrototypeChangesForMethodDefinition(method.getReference());
-    return internalBuildIR(method, appView, new NumberGenerator(), null, protoChanges);
+    return internalBuildIR(
+        method, appView, new NumberGenerator(), null, protoChanges, conversionOptions);
   }
 
   @Override
@@ -298,7 +300,13 @@
     assert valueNumberGenerator != null;
     assert callerPosition != null;
     assert protoChanges != null;
-    return internalBuildIR(method, appView, valueNumberGenerator, callerPosition, protoChanges);
+    return internalBuildIR(
+        method,
+        appView,
+        valueNumberGenerator,
+        callerPosition,
+        protoChanges,
+        MethodConversionOptions.nonConverting());
   }
 
   private IRCode internalBuildIR(
@@ -306,7 +314,8 @@
       AppView<?> appView,
       NumberGenerator valueNumberGenerator,
       Position callerPosition,
-      RewrittenPrototypeDescription protoChanges) {
+      RewrittenPrototypeDescription protoChanges,
+      MutableMethodConversionOptions conversionOptions) {
     LirCode<Integer> typedLir = asLirCode();
     return Lir2IRConverter.translate(
         method,
@@ -315,7 +324,8 @@
         appView,
         callerPosition,
         protoChanges,
-        appView.graphLens().getOriginalMethodSignature(method.getReference()));
+        appView.graphLens().getOriginalMethodSignature(method.getReference()),
+        conversionOptions);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
index a64d1c7..8919b83 100644
--- a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.IRToDexFinalizer;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.startup.generated.InstrumentationServerFactory;
@@ -99,7 +100,11 @@
 
     List<DexProgramClass> extraProgramClasses = createStartupRuntimeLibraryClasses();
     MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
-    converter.processClassesConcurrently(extraProgramClasses, eventConsumer, executorService);
+    converter.processClassesConcurrently(
+        extraProgramClasses,
+        eventConsumer,
+        MethodConversionOptions.forD8(appView),
+        executorService);
 
     DexApplication newApplication =
         appView.app().builder().addProgramClasses(extraProgramClasses).build();
@@ -167,7 +172,7 @@
     // Disable StringSwitch conversion to avoid having to run the StringSwitchRemover before
     // finalizing the code.
     MutableMethodConversionOptions conversionOptions =
-        new MutableMethodConversionOptions(options).disableStringSwitchConversion();
+        MethodConversionOptions.forD8(appView).disableStringSwitchConversion();
     IRCode code = method.buildIR(appView, conversionOptions);
     InstructionListIterator instructionIterator = code.entryBlock().listIterator(code);
     instructionIterator.positionBeforeNextInstructionThatMatches(not(Instruction::isArgument));
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 98bc585..9554981 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -108,6 +108,7 @@
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringCollection;
@@ -4917,7 +4918,7 @@
   }
 
   private void handleReflectiveBehavior(ProgramMethod method) {
-    IRCode code = method.buildIR(appView);
+    IRCode code = method.buildIR(appView, MethodConversionOptions.nonConverting());
     InstructionIterator iterator = code.instructionIterator();
     while (iterator.hasNext()) {
       Instruction instruction = iterator.next();
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
index 8f08d77..399bf81 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.ir.conversion.IRFinalizer;
 import com.android.tools.r8.ir.conversion.IRToCfFinalizer;
 import com.android.tools.r8.ir.conversion.IRToDexFinalizer;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.passes.ThrowCatchOptimizer;
 import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
@@ -264,8 +265,8 @@
     // Build IR.
     MutableMethodConversionOptions conversionOptions =
         mode.isInitialTreeShaking()
-            ? new MutableMethodConversionOptions(options).setIsGeneratingClassFiles(true)
-            : new MutableMethodConversionOptions(options);
+            ? MethodConversionOptions.forPreLirPhase(appView)
+            : MethodConversionOptions.forPostLirPhase(appView);
     conversionOptions.disableStringSwitchConversion();
 
     IRCode ir = method.buildIR(appView, conversionOptions);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index d8ef734..0fdb108 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2105,9 +2105,39 @@
     }
 
     public boolean canUseLir(AppView<?> appView) {
-      return useLir
-          && appView.enableWholeProgramOptimizations()
-          && !appView.options().protoShrinking().isProtoShrinkingEnabled();
+      return useLir && appView.enableWholeProgramOptimizations();
+    }
+
+    // As part of integrating LIR the compiler is split in three phases: pre, supported, and post.
+    // Any attempt at building IR must have conversion options consistent with the active phase.
+    private enum LirPhase {
+      PRE,
+      SUPPORTED,
+      POST
+    }
+
+    private LirPhase currentPhase = LirPhase.PRE;
+
+    public void enterLirSupportedPhase() {
+      assert isPreLirPhase();
+      currentPhase = LirPhase.SUPPORTED;
+    }
+
+    public void exitLirSupportedPhase() {
+      assert isSupportedLirPhase();
+      currentPhase = LirPhase.POST;
+    }
+
+    public boolean isPreLirPhase() {
+      return currentPhase == LirPhase.PRE;
+    }
+
+    public boolean isSupportedLirPhase() {
+      return currentPhase == LirPhase.SUPPORTED;
+    }
+
+    public boolean isPostLirPhase() {
+      return currentPhase == LirPhase.POST;
     }
 
     // If false, use the desugared library implementation when desugared library is enabled.
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 2cdeffd..e373513 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.MethodProcessorWithWave;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
@@ -94,7 +95,7 @@
 
     clazz.forEachProgramMethod(
         method -> {
-          IRCode code = method.buildIR(appView);
+          IRCode code = method.buildIR(appView, MethodConversionOptions.nonConverting());
           fieldAccessAnalysis.recordFieldAccesses(
               code, BytecodeMetadataProvider.builder(), feedback, new PrimaryMethodProcessorMock());
         });
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 60cad4c..93ad7a7 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
@@ -5,6 +5,8 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
@@ -22,7 +24,7 @@
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
 import com.android.tools.r8.ir.regalloc.LiveIntervals;
 import com.android.tools.r8.origin.Origin;
@@ -30,9 +32,22 @@
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.LinkedList;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class ConstantRemovalTest {
 
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return TestParameters.builder().withNoneRuntime().build();
+  }
+
+  public ConstantRemovalTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
   private static class MockLinearScanRegisterAllocator extends LinearScanRegisterAllocator {
     MockLinearScanRegisterAllocator(AppView<?> appView, IRCode code) {
       super(appView, code);
@@ -152,7 +167,7 @@
             basicBlockNumberGenerator,
             IRMetadata.unknown(),
             Origin.unknown(),
-            new MutableMethodConversionOptions(options));
+            MethodConversionOptions.nonConverting());
     PeepholeOptimizer.optimize(appView, code, new MockLinearScanRegisterAllocator(appView, code));
 
     // Check that all four constant number instructions remain.
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 9e02653..18c5121 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
@@ -28,7 +28,7 @@
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.code.Throw;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.passes.TrivialGotosCollapser;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
@@ -108,7 +108,7 @@
             basicBlockNumberGenerator,
             IRMetadata.unknown(),
             Origin.unknown(),
-            new MutableMethodConversionOptions(options));
+            MethodConversionOptions.forD8(appView));
     new TrivialGotosCollapser(appView).run(code, Timing.empty());
     assertTrue(code.entryBlock().isTrivialGoto());
     assertTrue(blocks.contains(block0));
@@ -197,7 +197,7 @@
             basicBlockNumberGenerator,
             IRMetadata.unknown(),
             Origin.unknown(),
-            new MutableMethodConversionOptions(options));
+            MethodConversionOptions.forD8(appView));
     new TrivialGotosCollapser(appView).run(code, Timing.empty());
     assertTrue(block0.getInstructions().get(1).isIf());
     assertEquals(block1, block0.getInstructions().get(1).asIf().fallthroughBlock());
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
index 983dfdb..d2bf214 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -20,7 +20,7 @@
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.List;
 import org.junit.Test;
@@ -126,6 +126,34 @@
     // not equivalent.
     assertFalse(
         add0.identicalAfterRegisterAllocation(
-            add1, allocator, new MutableMethodConversionOptions(allocator.options())));
+            add1,
+            allocator,
+            new MethodConversionOptions() {
+
+              @Override
+              public boolean isGeneratingDex() {
+                return true;
+              }
+
+              @Override
+              public boolean isGeneratingLir() {
+                return false;
+              }
+
+              @Override
+              public boolean isGeneratingClassFiles() {
+                return false;
+              }
+
+              @Override
+              public boolean isPeepholeOptimizationsEnabled() {
+                return false;
+              }
+
+              @Override
+              public boolean isStringSwitchConversionEnabled() {
+                return false;
+              }
+            }));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index cc336b6..9e90d86 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.signature.GenericSignatureParser;
@@ -74,7 +75,7 @@
   @Override
   public IRCode buildIR(AppView<?> appView) {
     assert codeInspector.application.options.programConsumer != null;
-    return getProgramMethod().buildIR(appView);
+    return getProgramMethod().buildIR(appView, MethodConversionOptions.forD8(appView));
   }
 
   @Override