Merge "Use mmapped boot image class table for PIC app HLoadClass."
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index c67c523..5ef6cbf 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -124,6 +124,7 @@
     kCall,
     kCallRelative,            // NOTE: Actual patching is instruction_set-dependent.
     kTypeRelative,            // NOTE: Actual patching is instruction_set-dependent.
+    kTypeClassTable,          // NOTE: Actual patching is instruction_set-dependent.
     kTypeBssEntry,            // NOTE: Actual patching is instruction_set-dependent.
     kStringRelative,          // NOTE: Actual patching is instruction_set-dependent.
     kStringInternTable,       // NOTE: Actual patching is instruction_set-dependent.
@@ -177,6 +178,16 @@
     return patch;
   }
 
+  static LinkerPatch TypeClassTablePatch(size_t literal_offset,
+                                         const DexFile* target_dex_file,
+                                         uint32_t pc_insn_offset,
+                                         uint32_t target_type_idx) {
+    LinkerPatch patch(literal_offset, Type::kTypeClassTable, target_dex_file);
+    patch.type_idx_ = target_type_idx;
+    patch.pc_insn_offset_ = pc_insn_offset;
+    return patch;
+  }
+
   static LinkerPatch TypeBssEntryPatch(size_t literal_offset,
                                        const DexFile* target_dex_file,
                                        uint32_t pc_insn_offset,
@@ -243,6 +254,7 @@
       case Type::kMethodBssEntry:
       case Type::kCallRelative:
       case Type::kTypeRelative:
+      case Type::kTypeClassTable:
       case Type::kTypeBssEntry:
       case Type::kStringRelative:
       case Type::kStringInternTable:
@@ -264,12 +276,14 @@
 
   const DexFile* TargetTypeDexFile() const {
     DCHECK(patch_type_ == Type::kTypeRelative ||
+           patch_type_ == Type::kTypeClassTable ||
            patch_type_ == Type::kTypeBssEntry);
     return target_dex_file_;
   }
 
   dex::TypeIndex TargetTypeIndex() const {
     DCHECK(patch_type_ == Type::kTypeRelative ||
+           patch_type_ == Type::kTypeClassTable ||
            patch_type_ == Type::kTypeBssEntry);
     return dex::TypeIndex(type_idx_);
   }
@@ -292,6 +306,7 @@
     DCHECK(patch_type_ == Type::kMethodRelative ||
            patch_type_ == Type::kMethodBssEntry ||
            patch_type_ == Type::kTypeRelative ||
+           patch_type_ == Type::kTypeClassTable ||
            patch_type_ == Type::kTypeBssEntry ||
            patch_type_ == Type::kStringRelative ||
            patch_type_ == Type::kStringInternTable ||
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 0ebabc1..4960f4d 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -61,6 +61,7 @@
     case LinkerPatch::Type::kMethodRelative:
     case LinkerPatch::Type::kMethodBssEntry:
     case LinkerPatch::Type::kTypeRelative:
+    case LinkerPatch::Type::kTypeClassTable:
     case LinkerPatch::Type::kTypeBssEntry:
     case LinkerPatch::Type::kStringRelative:
     case LinkerPatch::Type::kStringInternTable:
@@ -266,6 +267,7 @@
     } else {
       // LDR/STR 32-bit or 64-bit with imm12 == 0 (unset).
       DCHECK(patch.GetType() == LinkerPatch::Type::kMethodBssEntry ||
+             patch.GetType() == LinkerPatch::Type::kTypeClassTable ||
              patch.GetType() == LinkerPatch::Type::kTypeBssEntry ||
              patch.GetType() == LinkerPatch::Type::kStringInternTable ||
              patch.GetType() == LinkerPatch::Type::kStringBssEntry) << patch.GetType();
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index a33081e..cc8c6df 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -28,6 +28,7 @@
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
+#include "class_table-inl.h"
 #include "compiled_method.h"
 #include "debug/method_debug_info.h"
 #include "dex/verification_results.h"
@@ -772,7 +773,8 @@
         } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) {
           StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex());
           writer_->bss_string_entries_.Overwrite(ref, /* placeholder */ 0u);
-        } else if (patch.GetType() == LinkerPatch::Type::kStringInternTable) {
+        } else if (patch.GetType() == LinkerPatch::Type::kStringInternTable ||
+                   patch.GetType() == LinkerPatch::Type::kTypeClassTable) {
           writer_->map_boot_image_tables_to_bss_ = true;
         }
       }
@@ -1427,6 +1429,14 @@
                                                                      target_offset);
                 break;
               }
+              case LinkerPatch::Type::kTypeClassTable: {
+                uint32_t target_offset = GetClassTableEntryOffset(patch);
+                writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+                                                                     patch,
+                                                                     offset_ + literal_offset,
+                                                                     target_offset);
+                break;
+              }
               case LinkerPatch::Type::kTypeBssEntry: {
                 TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex());
                 uint32_t target_offset = writer_->bss_start_ + writer_->bss_type_entries_.Get(ref);
@@ -1621,11 +1631,25 @@
     const uint8_t* string_root = writer_->LookupBootImageInternTableSlot(
         *patch.TargetStringDexFile(), patch.TargetStringIndex());
     DCHECK(string_root != nullptr);
+    return GetBootImageTableEntryOffset(string_root);
+  }
+
+  // Calculate the offset of the ClassTable::TableSlot when mmapped to the .bss.
+  uint32_t GetClassTableEntryOffset(const LinkerPatch& patch)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(!writer_->HasBootImage());
+    const uint8_t* table_slot =
+        writer_->LookupBootImageClassTableSlot(*patch.TargetTypeDexFile(), patch.TargetTypeIndex());
+    DCHECK(table_slot != nullptr);
+    return GetBootImageTableEntryOffset(table_slot);
+  }
+
+  uint32_t GetBootImageTableEntryOffset(const uint8_t* raw_root) {
     uint32_t base_offset = writer_->bss_start_;
     for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
       const uint8_t* const_tables_begin =
           space->Begin() + space->GetImageHeader().GetBootImageConstantTablesOffset();
-      size_t offset = static_cast<size_t>(string_root - const_tables_begin);
+      size_t offset = static_cast<size_t>(raw_root - const_tables_begin);
       if (offset < space->GetImageHeader().GetBootImageConstantTablesSize()) {
         DCHECK_LE(base_offset + offset, writer_->bss_start_ + writer_->bss_methods_offset_);
         return base_offset + offset;
@@ -3542,8 +3566,7 @@
 
 const uint8_t* OatWriter::LookupBootImageInternTableSlot(const DexFile& dex_file,
                                                          dex::StringIndex string_idx)
-    NO_THREAD_SAFETY_ANALYSIS {
-  // Single-threaded OatWriter can avoid locking.
+    NO_THREAD_SAFETY_ANALYSIS {  // Single-threaded OatWriter can avoid locking.
   uint32_t utf16_length;
   const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length);
   DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data));
@@ -3561,4 +3584,20 @@
   UNREACHABLE();
 }
 
+const uint8_t* OatWriter::LookupBootImageClassTableSlot(const DexFile& dex_file,
+                                                        dex::TypeIndex type_idx)
+    NO_THREAD_SAFETY_ANALYSIS {  // Single-threaded OatWriter can avoid locking.
+  const char* descriptor = dex_file.StringByTypeIdx(type_idx);
+  ClassTable::DescriptorHashPair pair(descriptor, ComputeModifiedUtf8Hash(descriptor));
+  ClassTable* table = Runtime::Current()->GetClassLinker()->boot_class_table_.get();
+  for (const ClassTable::ClassSet& class_set : table->classes_) {
+    auto it = class_set.Find(pair);
+    if (it != class_set.end()) {
+      return reinterpret_cast<const uint8_t*>(std::addressof(*it));
+    }
+  }
+  LOG(FATAL) << "Did not find boot image class " << descriptor;
+  UNREACHABLE();
+}
+
 }  // namespace art
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 780dee0..7f2045f 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -336,6 +336,8 @@
   // Find the address of the GcRoot<String> in the InternTable for a boot image string.
   const uint8_t* LookupBootImageInternTableSlot(const DexFile& dex_file,
                                                 dex::StringIndex string_idx);
+  // Find the address of the ClassTable::TableSlot for a boot image class.
+  const uint8_t* LookupBootImageClassTableSlot(const DexFile& dex_file, dex::TypeIndex type_idx);
 
   enum class WriteState {
     kAddingDexFileSources,
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 1b62868..19e5d06 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -21,6 +21,7 @@
 #include "art_method.h"
 #include "base/bit_utils.h"
 #include "base/bit_utils_iterator.h"
+#include "class_table.h"
 #include "code_generator_utils.h"
 #include "compiled_method.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -4784,7 +4785,8 @@
                                                                   linker_patches);
   } else {
     DCHECK(pc_relative_method_patches_.empty());
-    DCHECK(pc_relative_type_patches_.empty());
+    EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(pc_relative_type_patches_,
+                                                                  linker_patches);
     EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(pc_relative_string_patches_,
                                                                      linker_patches);
   }
@@ -4861,6 +4863,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+    case HLoadClass::LoadKind::kBootImageClassTable:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -4972,6 +4975,25 @@
       __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
+    case HLoadClass::LoadKind::kBootImageClassTable: {
+      DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      // Add ADRP with its PC-relative type patch.
+      const DexFile& dex_file = cls->GetDexFile();
+      dex::TypeIndex type_index = cls->GetTypeIndex();
+      vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeTypePatch(dex_file, type_index);
+      codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
+      // Add LDR with its PC-relative type patch.
+      vixl::aarch64::Label* ldr_label =
+          codegen_->NewPcRelativeTypePatch(dex_file, type_index, adrp_label);
+      codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X());
+      // Extract the reference from the slot data, i.e. clear the hash bits.
+      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
+          ComputeModifiedUtf8Hash(dex_file.StringByTypeIdx(type_index)));
+      if (masked_hash != 0) {
+        __ Sub(out.W(), out.W(), Operand(masked_hash));
+      }
+      break;
+    }
     case HLoadClass::LoadKind::kBssEntry: {
       // Add ADRP with its PC-relative Class .bss entry patch.
       const DexFile& dex_file = cls->GetDexFile();
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 8288141..8b9495d 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -21,6 +21,7 @@
 #include "art_method.h"
 #include "base/bit_utils.h"
 #include "base/bit_utils_iterator.h"
+#include "class_table.h"
 #include "code_generator_utils.h"
 #include "common_arm.h"
 #include "compiled_method.h"
@@ -7122,6 +7123,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+    case HLoadClass::LoadKind::kBootImageClassTable:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -7234,6 +7236,20 @@
       __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
+    case HLoadClass::LoadKind::kBootImageClassTable: {
+      DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+          codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+      codegen_->EmitMovwMovtPlaceholder(labels, out);
+      __ Ldr(out, MemOperand(out, /* offset */ 0));
+      // Extract the reference from the slot data, i.e. clear the hash bits.
+      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
+          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
+      if (masked_hash != 0) {
+        __ Sub(out, out, Operand(masked_hash));
+      }
+      break;
+    }
     case HLoadClass::LoadKind::kBssEntry: {
       vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
           ? RegisterFrom(locations->GetTemp(0))
@@ -9214,7 +9230,8 @@
                                                                   linker_patches);
   } else {
     DCHECK(pc_relative_method_patches_.empty());
-    DCHECK(pc_relative_type_patches_.empty());
+    EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(pc_relative_type_patches_,
+                                                                  linker_patches);
     EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(pc_relative_string_patches_,
                                                                      linker_patches);
   }
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index ac8f675..f0ef007 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -20,6 +20,7 @@
 #include "arch/mips/entrypoints_direct_mips.h"
 #include "arch/mips/instruction_set_features_mips.h"
 #include "art_method.h"
+#include "class_table.h"
 #include "code_generator_utils.h"
 #include "compiled_method.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -1664,7 +1665,8 @@
                                                                   linker_patches);
   } else {
     DCHECK(pc_relative_method_patches_.empty());
-    DCHECK(pc_relative_type_patches_.empty());
+    EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(pc_relative_type_patches_,
+                                                                  linker_patches);
     EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(pc_relative_string_patches_,
                                                                      linker_patches);
   }
@@ -7413,6 +7415,7 @@
       fallback_load = false;
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+    case HLoadClass::LoadKind::kBootImageClassTable:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -7643,6 +7646,7 @@
     // We need an extra register for PC-relative literals on R2.
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageAddress:
+    case HLoadClass::LoadKind::kBootImageClassTable:
     case HLoadClass::LoadKind::kBssEntry:
       if (isR6) {
         break;
@@ -7741,6 +7745,24 @@
                      codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
+    case HLoadClass::LoadKind::kBootImageClassTable: {
+      DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
+          codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+      CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
+          codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
+      codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
+                                                     out,
+                                                     base_or_current_method_reg);
+      __ Lw(out, out, /* placeholder */ 0x5678, &info_low->label);
+      // Extract the reference from the slot data, i.e. clear the hash bits.
+      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
+          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
+      if (masked_hash != 0) {
+        __ Addiu(out, out, -masked_hash);
+      }
+      break;
+    }
     case HLoadClass::LoadKind::kBssEntry: {
       bss_info_high = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
       CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 71c2bff..201b1b0 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -18,6 +18,7 @@
 
 #include "arch/mips64/asm_support_mips64.h"
 #include "art_method.h"
+#include "class_table.h"
 #include "code_generator_utils.h"
 #include "compiled_method.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -1573,7 +1574,8 @@
                                                                   linker_patches);
   } else {
     DCHECK(pc_relative_method_patches_.empty());
-    DCHECK(pc_relative_type_patches_.empty());
+    EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(pc_relative_type_patches_,
+                                                                  linker_patches);
     EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(pc_relative_string_patches_,
                                                                      linker_patches);
   }
@@ -5767,6 +5769,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+    case HLoadClass::LoadKind::kBootImageClassTable:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -6016,6 +6019,22 @@
                      codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
+    case HLoadClass::LoadKind::kBootImageClassTable: {
+      DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
+          codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+      CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+          codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
+      codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
+      __ Lwu(out, AT, /* placeholder */ 0x5678);
+      // Extract the reference from the slot data, i.e. clear the hash bits.
+      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
+          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
+      if (masked_hash != 0) {
+        __ Daddiu(out, out, -masked_hash);
+      }
+      break;
+    }
     case HLoadClass::LoadKind::kBssEntry: {
       bss_info_high = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 512968f..e45ad0a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -17,6 +17,7 @@
 #include "code_generator_x86.h"
 
 #include "art_method.h"
+#include "class_table.h"
 #include "code_generator_utils.h"
 #include "compiled_method.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -4702,7 +4703,8 @@
     EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(string_patches_, linker_patches);
   } else {
     DCHECK(boot_image_method_patches_.empty());
-    DCHECK(boot_image_type_patches_.empty());
+    EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(boot_image_type_patches_,
+                                                                  linker_patches);
     EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(string_patches_,
                                                                      linker_patches);
   }
@@ -6038,6 +6040,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+    case HLoadClass::LoadKind::kBootImageClassTable:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -6075,6 +6078,7 @@
 
   if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
       load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative ||
+      load_kind == HLoadClass::LoadKind::kBootImageClassTable ||
       load_kind == HLoadClass::LoadKind::kBssEntry) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -6151,6 +6155,19 @@
       __ movl(out, Immediate(address));
       break;
     }
+    case HLoadClass::LoadKind::kBootImageClassTable: {
+      DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      Register method_address = locations->InAt(0).AsRegister<Register>();
+      __ movl(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset));
+      codegen_->RecordBootTypePatch(cls);
+      // Extract the reference from the slot data, i.e. clear the hash bits.
+      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
+          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
+      if (masked_hash != 0) {
+        __ subl(out, Immediate(masked_hash));
+      }
+      break;
+    }
     case HLoadClass::LoadKind::kBssEntry: {
       Register method_address = locations->InAt(0).AsRegister<Register>();
       Address address(method_address, CodeGeneratorX86::kDummy32BitOffset);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 0c3b2ad..8c4374d 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -17,6 +17,7 @@
 #include "code_generator_x86_64.h"
 
 #include "art_method.h"
+#include "class_table.h"
 #include "code_generator_utils.h"
 #include "compiled_method.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -1133,7 +1134,8 @@
     EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(string_patches_, linker_patches);
   } else {
     DCHECK(boot_image_method_patches_.empty());
-    DCHECK(boot_image_type_patches_.empty());
+    EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(boot_image_type_patches_,
+                                                                  linker_patches);
     EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(string_patches_,
                                                                      linker_patches);
   }
@@ -5456,6 +5458,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+    case HLoadClass::LoadKind::kBootImageClassTable:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -5564,6 +5567,18 @@
       __ movl(out, Immediate(static_cast<int32_t>(address)));  // Zero-extended.
       break;
     }
+    case HLoadClass::LoadKind::kBootImageClassTable: {
+      DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      __ movl(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false));
+      codegen_->RecordBootTypePatch(cls);
+      // Extract the reference from the slot data, i.e. clear the hash bits.
+      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
+          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
+      if (masked_hash != 0) {
+        __ subl(out, Immediate(masked_hash));
+      }
+      break;
+    }
     case HLoadClass::LoadKind::kBssEntry: {
       Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
                                           /* no_rip */ false);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 217a8f2..9cff6b0 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2777,6 +2777,7 @@
   }
   switch (GetLoadKind()) {
     case LoadKind::kBootImageAddress:
+    case LoadKind::kBootImageClassTable:
     case LoadKind::kJitTableAddress: {
       ScopedObjectAccess soa(Thread::Current());
       return GetClass().Get() == other_load_class->GetClass().Get();
@@ -2810,6 +2811,8 @@
       return os << "BootImageLinkTimePcRelative";
     case HLoadClass::LoadKind::kBootImageAddress:
       return os << "BootImageAddress";
+    case HLoadClass::LoadKind::kBootImageClassTable:
+      return os << "BootImageClassTable";
     case HLoadClass::LoadKind::kBssEntry:
       return os << "BssEntry";
     case HLoadClass::LoadKind::kJitTableAddress:
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 6bf53f7..a6d0da1 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -5676,6 +5676,10 @@
     // Used for boot image classes referenced by apps in AOT- and JIT-compiled code.
     kBootImageAddress,
 
+    // Use a PC-relative load from a boot image ClassTable mmapped into the .bss
+    // of the oat file.
+    kBootImageClassTable,
+
     // Load from an entry in the .bss section using a PC-relative load.
     // Used for classes outside boot image when .bss is accessible with a PC-relative load.
     kBssEntry,
@@ -5821,6 +5825,7 @@
   static bool HasTypeReference(LoadKind load_kind) {
     return load_kind == LoadKind::kReferrersClass ||
         load_kind == LoadKind::kBootImageLinkTimePcRelative ||
+        load_kind == LoadKind::kBootImageClassTable ||
         load_kind == LoadKind::kBssEntry ||
         load_kind == LoadKind::kRuntimeCall;
   }
@@ -5854,6 +5859,7 @@
   // including literal pool loads, which are PC-relative too.
   DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
          GetLoadKind() == LoadKind::kBootImageAddress ||
+         GetLoadKind() == LoadKind::kBootImageClassTable ||
          GetLoadKind() == LoadKind::kBssEntry) << GetLoadKind();
   DCHECK(special_input_.GetInstruction() == nullptr);
   special_input_ = HUserRecord<HInstruction*>(special_input);
diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc
index 4cb99f9..e569b78 100644
--- a/compiler/optimizing/pc_relative_fixups_mips.cc
+++ b/compiler/optimizing/pc_relative_fixups_mips.cc
@@ -75,6 +75,7 @@
     switch (load_kind) {
       case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
       case HLoadClass::LoadKind::kBootImageAddress:
+      case HLoadClass::LoadKind::kBootImageClassTable:
       case HLoadClass::LoadKind::kBssEntry:
         // Add a base register for PC-relative literals on R2.
         InitializePCRelativeBasePointer();
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index c463ecd..9877e10 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -83,6 +83,7 @@
   void VisitLoadClass(HLoadClass* load_class) OVERRIDE {
     HLoadClass::LoadKind load_kind = load_class->GetLoadKind();
     if (load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative ||
+        load_kind == HLoadClass::LoadKind::kBootImageClassTable ||
         load_kind == HLoadClass::LoadKind::kBssEntry) {
       HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_class);
       load_class->AddSpecialInput(method_address);
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 1ca63f4..e46c9a7 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -205,11 +205,15 @@
           // TODO(ngeoffray): Generate HDeoptimize instead.
           desired_load_kind = HLoadClass::LoadKind::kRuntimeCall;
         }
-      } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) {
-        // AOT app compilation. Check if the class is in the boot image.
-        desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
+      } else if (is_in_boot_image) {
+        // AOT app compilation, boot image class.
+        if (codegen->GetCompilerOptions().GetCompilePic()) {
+          desired_load_kind = HLoadClass::LoadKind::kBootImageClassTable;
+        } else {
+          desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
+        }
       } else {
-        // Not JIT and either the klass is not in boot image or we are compiling in PIC mode.
+        // Not JIT and the klass is not in boot image.
         desired_load_kind = HLoadClass::LoadKind::kBssEntry;
       }
     }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 8c2f0a5..9727adf 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -1278,7 +1278,7 @@
   friend class ImageWriter;  // for GetClassRoots
   friend class JniCompilerTest;  // for GetRuntimeQuickGenericJniStub
   friend class JniInternalTest;  // for GetRuntimeQuickGenericJniStub
-  friend class OatWriter;  // for boot image string table slot address lookup.
+  friend class OatWriter;  // for boot image string/class table slot address lookup.
   friend class VMClassLoader;  // for LookupClass and FindClassInBaseDexClassLoader.
   ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName);  // for DexLock, and RegisterDexFileLocked
   ART_FRIEND_TEST(mirror::DexCacheMethodHandlesTest, Open);  // for AllocDexCache
diff --git a/runtime/class_table.h b/runtime/class_table.h
index a259725..0ffe93e 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -287,6 +287,7 @@
   std::vector<const OatFile*> oat_files_ GUARDED_BY(lock_);
 
   friend class ImageWriter;  // for InsertWithoutLocks.
+  friend class OatWriter;  // for boot class TableSlot address lookup.
 };
 
 }  // namespace art
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 7a60bb5..1f1920c 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -277,27 +277,27 @@
 
   /// CHECK-START-X86: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
   // Note: load kind depends on PIC/non-PIC
-  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BssEntry}} class_name:java.lang.String
+  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String
 
   /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
   // Note: load kind depends on PIC/non-PIC
-  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BssEntry}} class_name:java.lang.String
+  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String
 
   /// CHECK-START-ARM: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
   // Note: load kind depends on PIC/non-PIC
-  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BssEntry}} class_name:java.lang.String
+  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String
 
   /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
   // Note: load kind depends on PIC/non-PIC
-  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BssEntry}} class_name:java.lang.String
+  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String
 
   /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
   // Note: load kind depends on PIC/non-PIC
-  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BssEntry}} class_name:java.lang.String
+  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String
 
   /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
   // Note: load kind depends on PIC/non-PIC
-  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BssEntry}} class_name:java.lang.String
+  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String
 
   public static Class<?> $noinline$getStringClass() {
     // Prevent inlining to avoid the string comparison being optimized away.