Add an empty enum unboxer to avoid null checks

Change-Id: I38ef60ef3219d5cc02707a0deb2326569885af0c
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
index bd42e7d..33829cd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxerImpl;
 
 /**
@@ -33,7 +34,7 @@
  * code to type check until lens code rewriting, which replaces the {@link NewUnboxedEnumInstance}
  * instructions by {@link ConstNumber} instructions.
  *
- * <p>Note: The {@link NewUnboxedEnumInstance} is only used from {@link EnumUnboxerImpl#unboxEnums}
+ * <p>Note: The {@link NewUnboxedEnumInstance} is only used from {@link EnumUnboxer#unboxEnums}
  * until the execution of the {@link com.android.tools.r8.ir.conversion.PostMethodProcessor}. There
  * should be no instances of {@link NewUnboxedEnumInstance} (nor {@link CfNewUnboxedEnum}, {@link
  * DexNewUnboxedEnumInstance}) after IR processing has finished.
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 83aa86a..a165209 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
@@ -78,8 +78,7 @@
 import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
 import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
-import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
-import com.android.tools.r8.ir.optimize.enums.EnumUnboxerImpl;
+import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
 import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoCollector;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -153,7 +152,7 @@
   private final TypeChecker typeChecker;
   private final ServiceLoaderRewriter serviceLoaderRewriter;
   private final EnumValueOptimizer enumValueOptimizer;
-  private final EnumUnboxerImpl enumUnboxer;
+  private final EnumUnboxer enumUnboxer;
 
   public final AssumeInserter assumeInserter;
   private final DynamicTypeOptimization dynamicTypeOptimization;
@@ -239,7 +238,7 @@
       this.serviceLoaderRewriter = null;
       this.methodOptimizationInfoCollector = null;
       this.enumValueOptimizer = null;
-      this.enumUnboxer = null;
+      this.enumUnboxer = EnumUnboxer.empty();
       this.assumeInserter = null;
       return;
     }
@@ -267,8 +266,7 @@
           options.enableTreeShakingOfLibraryMethodOverrides
               ? new LibraryMethodOverrideAnalysis(appViewWithLiveness)
               : null;
-      this.enumUnboxer =
-          options.enableEnumUnboxing ? new EnumUnboxerImpl(appViewWithLiveness) : null;
+      this.enumUnboxer = EnumUnboxer.create(appViewWithLiveness);
       this.lensCodeRewriter = new LensCodeRewriter(appViewWithLiveness, enumUnboxer);
       this.inliner = new Inliner(appViewWithLiveness, lensCodeRewriter);
       this.outliner = Outliner.create(appViewWithLiveness);
@@ -307,7 +305,7 @@
       this.serviceLoaderRewriter = null;
       this.methodOptimizationInfoCollector = null;
       this.enumValueOptimizer = null;
-      this.enumUnboxer = null;
+      this.enumUnboxer = EnumUnboxer.empty();
     }
     this.stringSwitchRemover =
         options.isStringSwitchConversionEnabled()
@@ -641,10 +639,7 @@
           optimization.abandonCallSitePropagationForPinnedMethodsAndOverrides(
               executorService, timing);
         });
-    ConsumerUtils.acceptIfNotNull(
-        enumUnboxer,
-        enumUnboxer ->
-            enumUnboxer.initializeEnumUnboxingCandidates(graphLensForPrimaryOptimizationPass));
+    enumUnboxer.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
     ConsumerUtils.acceptIfNotNull(
         classStaticizer,
         classStaticizer ->
@@ -725,12 +720,8 @@
           .run(executorService, feedback, timing);
     }
 
-    if (enumUnboxer != null) {
-      outliner.rewriteWithLens();
-      enumUnboxer.unboxEnums(this, postMethodProcessorBuilder, executorService, feedback);
-    } else {
-      appView.setUnboxedEnums(EnumDataMap.empty());
-    }
+    outliner.rewriteWithLens();
+    enumUnboxer.unboxEnums(appView, this, postMethodProcessorBuilder, executorService, feedback);
 
     GraphLens graphLensForSecondaryOptimizationPass = appView.graphLens();
 
@@ -758,9 +749,7 @@
     }
     timing.end();
 
-    if (enumUnboxer != null) {
-      enumUnboxer.unsetRewriter();
-    }
+    enumUnboxer.unsetRewriter();
 
     // All the code that should be impacted by the lenses inserted between phase 1 and phase 2
     // have now been processed and rewritten, we clear code lens rewriting so that the class
@@ -849,9 +838,7 @@
     if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
       appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues();
     }
-    if (enumUnboxer != null) {
-      enumUnboxer.updateEnumUnboxingCandidatesInfo();
-    }
+    enumUnboxer.updateEnumUnboxingCandidatesInfo();
     assert delayedOptimizationFeedback.noUpdatesLeft();
     onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
     onWaveDoneActions = null;
@@ -1549,7 +1536,7 @@
     appView.withArgumentPropagator(
         argumentPropagator -> argumentPropagator.scan(method, code, methodProcessor, timing));
 
-    if (enumUnboxer != null && methodProcessor.isPrimaryMethodProcessor()) {
+    if (methodProcessor.isPrimaryMethodProcessor()) {
       enumUnboxer.analyzeEnums(code, conversionOptions);
     }
 
@@ -1594,9 +1581,7 @@
                 appView, code, classInitializerDefaultsResult, feedback, timing);
       }
     }
-    if (enumUnboxer != null) {
-      enumUnboxer.recordEnumState(method.getHolder(), staticFieldValues);
-    }
+    enumUnboxer.recordEnumState(method.getHolder(), staticFieldValues);
     if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
       appView
           .protoShrinker()
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 10ea8e3..0356381 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
@@ -93,7 +93,7 @@
 import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.optimize.enums.EnumUnboxerImpl;
+import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
 import com.android.tools.r8.utils.InternalOptions;
@@ -110,12 +110,11 @@
 public class LensCodeRewriter {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
-  private final EnumUnboxerImpl enumUnboxer;
+  private final EnumUnboxer enumUnboxer;
   private final LensCodeRewriterUtils helper;
   private final InternalOptions options;
 
-  LensCodeRewriter(
-      AppView<? extends AppInfoWithClassHierarchy> appView, EnumUnboxerImpl enumUnboxer) {
+  LensCodeRewriter(AppView<? extends AppInfoWithClassHierarchy> appView, EnumUnboxer enumUnboxer) {
     this.appView = appView;
     this.enumUnboxer = enumUnboxer;
     this.helper = new LensCodeRewriterUtils(appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
new file mode 100644
index 0000000..2fb3b5a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.enums;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor.Builder;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+public class EmptyEnumUnboxer extends EnumUnboxer {
+
+  private static final EmptyEnumUnboxer INSTANCE = new EmptyEnumUnboxer();
+
+  private EmptyEnumUnboxer() {}
+
+  static EmptyEnumUnboxer get() {
+    return INSTANCE;
+  }
+
+  @Override
+  public void prepareForPrimaryOptimizationPass(GraphLens graphLensForPrimaryOptimizationPass) {
+    // Intentionally empty.
+  }
+
+  @Override
+  public void analyzeEnums(IRCode code, MutableMethodConversionOptions conversionOptions) {
+    // Intentionally empty.
+  }
+
+  @Override
+  public void recordEnumState(DexProgramClass clazz, StaticFieldValues staticFieldValues) {
+    // Intentionally empty.
+  }
+
+  @Override
+  public Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
+    // Intentionally empty.
+    return Collections.emptySet();
+  }
+
+  @Override
+  public void unboxEnums(
+      AppView<AppInfoWithLiveness> appView,
+      IRConverter converter,
+      Builder postMethodProcessorBuilder,
+      ExecutorService executorService,
+      OptimizationFeedbackDelayed feedback) {
+    appView.setUnboxedEnums(EnumDataMap.empty());
+  }
+
+  @Override
+  public void unsetRewriter() {
+    // Intentionally empty.
+  }
+
+  @Override
+  public void updateEnumUnboxingCandidatesInfo() {
+    // Intentionally empty.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
new file mode 100644
index 0000000..2cdf22f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.enums;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor.Builder;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public abstract class EnumUnboxer {
+
+  public static EnumUnboxer create(AppView<AppInfoWithLiveness> appView) {
+    return appView.options().enableEnumUnboxing ? new EnumUnboxerImpl(appView) : empty();
+  }
+
+  public static EmptyEnumUnboxer empty() {
+    return EmptyEnumUnboxer.get();
+  }
+
+  public abstract void prepareForPrimaryOptimizationPass(
+      GraphLens graphLensForPrimaryOptimizationPass);
+
+  public abstract void analyzeEnums(IRCode code, MutableMethodConversionOptions conversionOptions);
+
+  public abstract void recordEnumState(DexProgramClass clazz, StaticFieldValues staticFieldValues);
+
+  public abstract Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor);
+
+  public abstract void unboxEnums(
+      AppView<AppInfoWithLiveness> appView,
+      IRConverter converter,
+      Builder postMethodProcessorBuilder,
+      ExecutorService executorService,
+      OptimizationFeedbackDelayed feedback)
+      throws ExecutionException;
+
+  public abstract void unsetRewriter();
+
+  public abstract void updateEnumUnboxingCandidatesInfo();
+}
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 fdeea23..46eec15 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
@@ -76,7 +76,7 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
-import com.android.tools.r8.ir.conversion.PostMethodProcessor;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor.Builder;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
 import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
@@ -131,7 +131,7 @@
 import java.util.function.Function;
 import java.util.function.Predicate;
 
-public class EnumUnboxerImpl {
+public class EnumUnboxerImpl extends EnumUnboxer {
 
   private final AppView<AppInfoWithLiveness> appView;
   private final DexItemFactory factory;
@@ -157,7 +157,7 @@
   private final boolean debugLogEnabled;
   private final Map<DexType, List<Reason>> debugLogs;
 
-  public EnumUnboxerImpl(AppView<AppInfoWithLiveness> appView) {
+  EnumUnboxerImpl(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
     if (appView.options().testing.enableEnumUnboxingDebugLogs) {
@@ -180,6 +180,7 @@
     return ordinalField;
   }
 
+  @Override
   public void updateEnumUnboxingCandidatesInfo() {
     for (DexProgramClass candidate : candidatesToRemoveInWave) {
       enumUnboxingCandidatesInfo.removeCandidate(candidate);
@@ -232,6 +233,7 @@
     return enumUnboxingCandidatesInfo.getCandidateClassOrNull(type);
   }
 
+  @Override
   public void analyzeEnums(IRCode code, MutableMethodConversionOptions conversionOptions) {
     Set<DexType> eligibleEnums = Sets.newIdentityHashSet();
     for (BasicBlock block : code.blocks) {
@@ -556,7 +558,12 @@
     return result;
   }
 
-  public void initializeEnumUnboxingCandidates(GraphLens graphLensForPrimaryOptimizationPass) {
+  @Override
+  public void prepareForPrimaryOptimizationPass(GraphLens graphLensForPrimaryOptimizationPass) {
+    initializeEnumUnboxingCandidates(graphLensForPrimaryOptimizationPass);
+  }
+
+  private void initializeEnumUnboxingCandidates(GraphLens graphLensForPrimaryOptimizationPass) {
     assert enumUnboxingCandidatesInfo == null;
     enumUnboxingCandidatesInfo =
         new EnumUnboxingCandidateAnalysis(appView, this)
@@ -566,9 +573,11 @@
             graphLensForPrimaryOptimizationPass);
   }
 
+  @Override
   public void unboxEnums(
+      AppView<AppInfoWithLiveness> appView,
       IRConverter converter,
-      PostMethodProcessor.Builder postBuilder,
+      Builder postBuilder,
       ExecutorService executorService,
       OptimizationFeedbackDelayed feedback)
       throws ExecutionException {
@@ -871,6 +880,7 @@
     return OptionalInt.empty();
   }
 
+  @Override
   public void recordEnumState(DexProgramClass clazz, StaticFieldValues staticFieldValues) {
     if (staticFieldValues == null || !staticFieldValues.isEnumStaticFieldValues()) {
       return;
@@ -1560,6 +1570,7 @@
     return false;
   }
 
+  @Override
   public Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
     // This has no effect during primary processing since the enumUnboxerRewriter is set
     // in between primary and post processing.
@@ -1569,6 +1580,7 @@
     return Sets.newIdentityHashSet();
   }
 
+  @Override
   public void unsetRewriter() {
     enumUnboxerRewriter = null;
   }