Rewrite image type in CompilerOptions.

Prepare for introduction of boot image extension.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: Pixel 2 XL boots.
Change-Id: Id007c4aeb4ada84fe65c1148fdf67e8a412e5b74
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 599f4aa..66421e2 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -184,7 +184,7 @@
 void CommonCompilerTest::CreateCompilerDriver() {
   ApplyInstructionSet();
 
-  compiler_options_->boot_image_ = true;
+  compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage;
   compiler_options_->compile_pic_ = false;  // Non-PIC boot image is a test configuration.
   compiler_options_->SetCompilerFilter(GetCompilerFilter());
   compiler_options_->image_classes_.swap(*GetImageClasses());
@@ -345,7 +345,7 @@
 }
 
 void CommonCompilerTest::ClearBootImageOption() {
-  compiler_options_->boot_image_ = false;
+  compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
 }
 
 }  // namespace art
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 4f83d60..f61e6c4 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -41,7 +41,7 @@
   void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
     TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
     TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
-    compiler_options_->boot_image_ = false;
+    compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
     compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken);
     // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
     // the results for all the dex files, not just the results for the current dex file.
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 685cde3..3610f18 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -44,8 +44,7 @@
       no_inline_from_(),
       dex_files_for_oat_file_(),
       image_classes_(),
-      boot_image_(false),
-      app_image_(false),
+      image_type_(ImageType::kNone),
       compiling_with_core_image_(false),
       baseline_(false),
       debuggable_(false),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 2f4e542..12fa251 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -58,6 +58,12 @@
   static const size_t kDefaultInlineMaxCodeUnits = 32;
   static constexpr size_t kUnsetInlineMaxCodeUnits = -1;
 
+  enum class ImageType : uint8_t {
+    kNone,                // JIT or AOT app compilation producing only an oat file but no image.
+    kBootImage,           // Creating boot image.
+    kAppImage,            // Creating app image.
+  };
+
   CompilerOptions();
   ~CompilerOptions();
 
@@ -191,7 +197,7 @@
 
   // Are we compiling a boot image?
   bool IsBootImage() const {
-    return boot_image_;
+    return image_type_ == ImageType::kBootImage;
   }
 
   bool IsBaseline() const {
@@ -200,11 +206,7 @@
 
   // Are we compiling an app image?
   bool IsAppImage() const {
-    return app_image_;
-  }
-
-  void DisableAppImage() {
-    app_image_ = false;
+    return image_type_ == ImageType::kAppImage;
   }
 
   // Returns whether we are compiling against a "core" image, which
@@ -356,8 +358,7 @@
   // Must not be empty for real boot image, only for tests pretending to compile boot image.
   HashSet<std::string> image_classes_;
 
-  bool boot_image_;
-  bool app_image_;
+  ImageType image_type_;
   bool compiling_with_core_image_;
   bool baseline_;
   bool debuggable_;
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index e1b23cc..eb44dd7 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -79,7 +79,7 @@
   }
 
   void SetupCompilerDriver() {
-    compiler_options_->boot_image_ = false;
+    compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
     compiler_driver_->InitializeThreadPools();
   }
 
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index dc123e4..12a8354 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -729,11 +729,15 @@
 
   void ProcessOptions(ParserOptions* parser_options) {
     compiler_options_->compile_pic_ = true;  // All AOT compilation is PIC.
-    compiler_options_->boot_image_ = !image_filenames_.empty();
-    compiler_options_->app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
-
-    if (IsAppImage() && IsBootImage()) {
-      Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
+    DCHECK(compiler_options_->image_type_ == CompilerOptions::ImageType::kNone);
+    if (!image_filenames_.empty()) {
+      compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage;
+    }
+    if (app_image_fd_ != -1 || !app_image_file_name_.empty()) {
+      if (compiler_options_->IsBootImage()) {
+        Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
+      }
+      compiler_options_->image_type_ = CompilerOptions::ImageType::kAppImage;
     }
 
     if (oat_filenames_.empty() && oat_fd_ == -1) {
@@ -1595,7 +1599,7 @@
     // If we need to downgrade the compiler-filter for size reasons.
     if (!IsBootImage() && IsVeryLarge(dex_files)) {
       // Disable app image to make sure dex2oat unloading is enabled.
-      compiler_options_->DisableAppImage();
+      compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
 
       // If we need to downgrade the compiler-filter for size reasons, do that early before we read
       // it below for creating verification callbacks.
@@ -1957,7 +1961,6 @@
 
       image_writer_.reset(new linker::ImageWriter(*compiler_options_,
                                                   image_base_,
-                                                  IsAppImage(),
                                                   image_storage_mode_,
                                                   oat_filenames_,
                                                   dex_file_oat_index_map_,
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 9d1a4e7..9ef2875 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -216,11 +216,10 @@
   // TODO: compile_pic should be a test argument.
   std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_options_,
                                                       kRequestedImageBase,
-                                                      /*compile_app_image*/false,
                                                       storage_mode,
                                                       oat_filename_vector,
                                                       dex_file_to_oat_index_map,
-                                                      /*dirty_image_objects*/nullptr));
+                                                      /*dirty_image_objects=*/ nullptr));
   {
     {
       jobject class_loader = nullptr;
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index e4fbd17..a3fc1cd 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -145,10 +145,15 @@
 // Separate objects into multiple bins to optimize dirty memory use.
 static constexpr bool kBinObjects = true;
 
+ObjPtr<mirror::ClassLoader> ImageWriter::GetClassLoader() {
+  CHECK_EQ(class_loaders_.size(), compiler_options_.IsAppImage() ? 1u : 0u);
+  return compiler_options_.IsAppImage() ? *class_loaders_.begin() : nullptr;
+}
+
 // Return true if an object is already in an image space.
 bool ImageWriter::IsInBootImage(const void* obj) const {
   gc::Heap* const heap = Runtime::Current()->GetHeap();
-  if (!compile_app_image_) {
+  if (compiler_options_.IsBootImage()) {
     DCHECK(heap->GetBootImageSpaces().empty());
     return false;
   }
@@ -165,7 +170,7 @@
 
 bool ImageWriter::IsInBootOatFile(const void* ptr) const {
   gc::Heap* const heap = Runtime::Current()->GetHeap();
-  if (!compile_app_image_) {
+  if (compiler_options_.IsBootImage()) {
     DCHECK(heap->GetBootImageSpaces().empty());
     return false;
   }
@@ -204,7 +209,7 @@
       PruneNonImageClasses();  // Remove junk
     }
 
-    if (compile_app_image_) {
+    if (compiler_options_.IsAppImage()) {
       TimingLogger::ScopedTiming t("ClearDexFileCookies", timings);
       // Clear dex file cookies for app images to enable app image determinism. This is required
       // since the cookie field contains long pointers to DexFiles which are not deterministic.
@@ -226,7 +231,7 @@
   // Used to store information that will later be used to calculate image
   // offsets to string references in the AppImage.
   std::vector<HeapReferencePointerInfo> string_ref_info;
-  if (ClassLinker::kAppImageMayContainStrings && compile_app_image_) {
+  if (ClassLinker::kAppImageMayContainStrings && compiler_options_.IsAppImage()) {
     // Count the number of string fields so we can allocate the appropriate
     // amount of space in the image section.
     TimingLogger::ScopedTiming t("AppImage:CollectStringReferenceInfo", timings);
@@ -248,7 +253,7 @@
   }
 
   // Obtain class count for debugging purposes
-  if (VLOG_IS_ON(compiler) && compile_app_image_) {
+  if (VLOG_IS_ON(compiler) && compiler_options_.IsAppImage()) {
     ScopedObjectAccess soa(self);
 
     size_t app_image_class_count  = 0;
@@ -268,7 +273,7 @@
     VLOG(compiler) << "Dex2Oat:AppImage:classCount = " << app_image_class_count;
   }
 
-  if (ClassLinker::kAppImageMayContainStrings && compile_app_image_) {
+  if (ClassLinker::kAppImageMayContainStrings && compiler_options_.IsAppImage()) {
     // Use the string reference information obtained earlier to calculate image
     // offsets.  These will later be written to the image by Write/CopyMetadata.
     TimingLogger::ScopedTiming t("AppImage:CalculateImageOffsets", timings);
@@ -580,7 +585,7 @@
 }
 
 void ImageWriter::CopyMetadata() {
-  CHECK(compile_app_image_);
+  DCHECK(compiler_options_.IsAppImage());
   CHECK_EQ(image_infos_.size(), 1u);
 
   const ImageInfo& image_info = image_infos_.back();
@@ -620,7 +625,7 @@
     ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
     std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
     for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
-      if (compile_app_image_ && IsInBootImage(dex_cache.Ptr())) {
+      if (IsInBootImage(dex_cache.Ptr())) {
         continue;  // Boot image DexCache is not written to the app image.
       }
       PreloadDexCache(dex_cache, class_loader);
@@ -642,7 +647,7 @@
     CopyAndFixupObjects();
   }
 
-  if (compile_app_image_) {
+  if (compiler_options_.IsAppImage()) {
     CopyMetadata();
   }
 
@@ -670,7 +675,7 @@
       return false;
     }
 
-    if (!compile_app_image_ && fchmod(image_file->Fd(), 0644) != 0) {
+    if (!compiler_options_.IsAppImage() && fchmod(image_file->Fd(), 0644) != 0) {
       PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
       image_file->Erase();
       return EXIT_FAILURE;
@@ -1223,7 +1228,7 @@
     std::unordered_set<mirror::Object*>* visited) {
   DCHECK(early_exit != nullptr);
   DCHECK(visited != nullptr);
-  DCHECK(compile_app_image_);
+  DCHECK(compiler_options_.IsAppImage());
   if (klass == nullptr || IsInBootImage(klass.Ptr())) {
     return false;
   }
@@ -1327,7 +1332,8 @@
   if (klass == nullptr) {
     return false;
   }
-  if (compile_app_image_ && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+  if (!compiler_options_.IsBootImage() &&
+      Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
     // Already in boot image, return true.
     return true;
   }
@@ -1335,7 +1341,7 @@
   if (!compiler_options_.IsImageClass(klass->GetDescriptor(&temp))) {
     return false;
   }
-  if (compile_app_image_) {
+  if (compiler_options_.IsAppImage()) {
     // For app images, we need to prune boot loader classes that are not in the boot image since
     // these may have already been loaded when the app image is loaded.
     // Keep classes in the boot image space since we don't want to re-resolve these.
@@ -1631,7 +1637,7 @@
     VisitClassLoaders(&class_loader_visitor);
     VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes";
     class_loader = class_loader_visitor.GetClassLoader();
-    DCHECK_EQ(class_loader != nullptr, compile_app_image_);
+    DCHECK_EQ(class_loader != nullptr, compiler_options_.IsAppImage());
   }
 
   // Clear references to removed classes from the DexCaches.
@@ -1694,7 +1700,7 @@
       return found.Ptr();
     }
   }
-  if (compile_app_image_) {
+  if (!compiler_options_.IsBootImage()) {
     Runtime* const runtime = Runtime::Current();
     ObjPtr<mirror::String> found = runtime->GetInternTable()->LookupStrong(self, string);
     // If we found it in the runtime intern table it could either be in the boot image or interned
@@ -1791,7 +1797,7 @@
   Handle<ObjectArray<Object>> dex_caches(hs.NewHandle(CollectDexCaches(self, oat_index)));
 
   // build an Object[] of the roots needed to restore the runtime
-  int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
+  int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compiler_options_.IsAppImage());
   Handle<ObjectArray<Object>> image_roots(hs.NewHandle(ObjectArray<Object>::Alloc(
       self, GetClassRoot<ObjectArray<Object>>(class_linker), image_roots_size)));
   image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
@@ -1804,14 +1810,14 @@
                           runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
   image_roots->Set<false>(ImageHeader::kNoClassDefFoundError,
                           runtime->GetPreAllocatedNoClassDefFoundError());
-  if (!compile_app_image_) {
+  if (!compiler_options_.IsAppImage()) {
     DCHECK(boot_image_live_objects != nullptr);
     image_roots->Set<false>(ImageHeader::kBootImageLiveObjects, boot_image_live_objects.Get());
   } else {
     DCHECK(boot_image_live_objects == nullptr);
   }
-  for (int32_t i = 0, num = ImageHeader::NumberOfImageRoots(compile_app_image_); i != num; ++i) {
-    if (compile_app_image_ && i == ImageHeader::kAppImageClassLoader) {
+  for (int32_t i = 0; i != image_roots_size; ++i) {
+    if (compiler_options_.IsAppImage() && i == ImageHeader::kAppImageClassLoader) {
       // image_roots[ImageHeader::kAppImageClassLoader] will be set later for app image.
       continue;
     }
@@ -1848,7 +1854,7 @@
       mirror::Class* as_klass = obj->AsClass();
       mirror::DexCache* dex_cache = as_klass->GetDexCache();
       DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus();
-      if (compile_app_image_) {
+      if (compiler_options_.IsAppImage()) {
         // Extra sanity, no boot loader classes should be left!
         CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass();
       }
@@ -1859,7 +1865,7 @@
       // belongs.
       oat_index = GetOatIndexForDexCache(dex_cache);
       ImageInfo& image_info = GetImageInfo(oat_index);
-      if (!compile_app_image_) {
+      if (!compiler_options_.IsAppImage()) {
         // Note: Avoid locking to prevent lock order violations from root visiting;
         // image_info.class_table_ is only accessed from the image writer.
         image_info.class_table_->InsertWithoutLocks(as_klass);
@@ -1962,7 +1968,7 @@
       // class loader.
       mirror::ClassLoader* class_loader = obj->AsClassLoader();
       if (class_loader->GetClassTable() != nullptr) {
-        DCHECK(compile_app_image_);
+        DCHECK(compiler_options_.IsAppImage());
         DCHECK(class_loaders_.empty());
         class_loaders_.insert(class_loader);
         ImageInfo& image_info = GetImageInfo(oat_index);
@@ -2140,7 +2146,7 @@
   Runtime* const runtime = Runtime::Current();
   VariableSizedHandleScope handles(self);
   MutableHandle<ObjectArray<Object>> boot_image_live_objects = handles.NewHandle(
-      compile_app_image_
+      compiler_options_.IsAppImage()
           ? nullptr
           : IntrinsicObjects::AllocateBootImageLiveObjects(self, runtime->GetClassLinker()));
   std::vector<Handle<ObjectArray<Object>>> image_roots;
@@ -2175,7 +2181,8 @@
   for (auto* m : image_methods_) {
     CHECK(m != nullptr);
     CHECK(m->IsRuntimeMethod());
-    DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image";
+    DCHECK_EQ(!compiler_options_.IsBootImage(), IsInBootImage(m))
+        << "Trampolines should be in boot image";
     if (!IsInBootImage(m)) {
       AssignMethodOffset(m, NativeObjectRelocationType::kRuntimeMethod, GetDefaultOatIndex());
     }
@@ -2227,7 +2234,7 @@
   // For app images, there may be objects that are only held live by the boot image. One
   // example is finalizer references. Forward these objects so that EnsureBinSlotAssignedCallback
   // does not fail any checks.
-  if (compile_app_image_) {
+  if (compiler_options_.IsAppImage()) {
     for (gc::space::ImageSpace* space : heap->GetBootImageSpaces()) {
       DCHECK(space->IsImageSpace());
       gc::accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
@@ -2528,7 +2535,7 @@
 }
 
 const void* ImageWriter::GetIntrinsicReferenceAddress(uint32_t intrinsic_data) {
-  DCHECK(!compile_app_image_);
+  DCHECK(compiler_options_.IsBootImage());
   switch (IntrinsicObjects::DecodePatchType(intrinsic_data)) {
     case IntrinsicObjects::PatchType::kIntegerValueOfArray: {
       const uint8_t* base_address =
@@ -2921,7 +2928,7 @@
   FixupClassVisitor visitor(this, copy);
   ObjPtr<mirror::Object>(orig)->VisitReferences(visitor, visitor);
 
-  if (kBitstringSubtypeCheckEnabled && compile_app_image_) {
+  if (kBitstringSubtypeCheckEnabled && compiler_options_.IsAppImage()) {
     // When we call SubtypeCheck::EnsureInitialize, it Assigns new bitstring
     // values to the parent of that class.
     //
@@ -3102,7 +3109,7 @@
 const uint8_t* ImageWriter::GetOatAddress(StubType type) const {
   DCHECK_LE(type, StubType::kLast);
   // If we are compiling an app image, we need to use the stubs of the boot image.
-  if (compile_app_image_) {
+  if (!compiler_options_.IsBootImage()) {
     // Use the current image pointers.
     const std::vector<gc::space::ImageSpace*>& image_spaces =
         Runtime::Current()->GetHeap()->GetBootImageSpaces();
@@ -3352,7 +3359,7 @@
   cur_image_info.oat_data_begin_ = cur_image_info.oat_file_begin_ + oat_data_offset;
   cur_image_info.oat_size_ = oat_data_size;
 
-  if (compile_app_image_) {
+  if (compiler_options_.IsAppImage()) {
     CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image.";
     return;
   }
@@ -3391,7 +3398,6 @@
 ImageWriter::ImageWriter(
     const CompilerOptions& compiler_options,
     uintptr_t image_begin,
-    bool compile_app_image,
     ImageHeader::StorageMode image_storage_mode,
     const std::vector<const char*>& oat_filenames,
     const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
@@ -3399,7 +3405,6 @@
     : compiler_options_(compiler_options),
       global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
       image_objects_offset_begin_(0),
-      compile_app_image_(compile_app_image),
       target_ptr_size_(InstructionSetPointerSize(compiler_options.GetInstructionSet())),
       image_infos_(oat_filenames.size()),
       dirty_methods_(0u),
@@ -3409,9 +3414,11 @@
       oat_filenames_(oat_filenames),
       dex_file_oat_index_map_(dex_file_oat_index_map),
       dirty_image_objects_(dirty_image_objects) {
+  DCHECK(compiler_options.IsBootImage() || compiler_options.IsAppImage());
   CHECK_NE(image_begin, 0U);
   std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
-  CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
+  CHECK_EQ(compiler_options.IsBootImage(),
+           Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
       << "Compiling a boot image should occur iff there are no boot image spaces loaded";
 }
 
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index bf89665..3c377a3 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -78,7 +78,6 @@
  public:
   ImageWriter(const CompilerOptions& compiler_options,
               uintptr_t image_begin,
-              bool compile_app_image,
               ImageHeader::StorageMode image_storage_mode,
               const std::vector<const char*>& oat_filenames,
               const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
@@ -112,10 +111,7 @@
     return true;
   }
 
-  ObjPtr<mirror::ClassLoader> GetClassLoader() {
-    CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
-    return compile_app_image_ ? *class_loaders_.begin() : nullptr;
-  }
+  ObjPtr<mirror::ClassLoader> GetClassLoader();
 
   template <typename T>
   T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -754,9 +750,6 @@
   // Oat index map for objects.
   std::unordered_map<mirror::Object*, uint32_t> oat_index_map_;
 
-  // Boolean flags.
-  const bool compile_app_image_;
-
   // Size of pointers on the target architecture.
   PointerSize target_ptr_size_;