Faster access to unresolved classes from compiled code.

Add two new load kinds to LoadClass, similar to kBssEntry
but using the access-checking entrypoint on the slow-path.
One is used for classes that are in the literal package and
the other for classes outside the literal package of the
compiling class. Associate new .bss entries with these load
kinds and update them from entrypoints based on the resolved
class properties. If the resolved class is public, both
types of entries can be updated, otherwise only the package
local entry can be updated and only if the defining class
loader of the class is the same as the caller's defining
class loader (which is identical for all code in an oat
file) because the run time access check for same package
requires both class loader and literal package name match.

Test: Additional tests in 727-checker-unresolved-class.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: aosp_blueline-userdebug boots.
Test: run-gtests.sh
Test: testrunner.py --target --optimizing
Bug: 161898207
Change-Id: I281e06ac2825caf81c6d7ee3128af833abd39992
diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h
index 7fedf2e..4000fc2 100644
--- a/compiler/linker/linker_patch.h
+++ b/compiler/linker/linker_patch.h
@@ -50,6 +50,8 @@
     kCallRelative,
     kTypeRelative,
     kTypeBssEntry,
+    kPublicTypeBssEntry,
+    kPackageTypeBssEntry,
     kStringRelative,
     kStringBssEntry,
     kCallEntrypoint,
@@ -122,6 +124,26 @@
     return patch;
   }
 
+  static LinkerPatch PublicTypeBssEntryPatch(size_t literal_offset,
+                                             const DexFile* target_dex_file,
+                                             uint32_t pc_insn_offset,
+                                             uint32_t target_type_idx) {
+    LinkerPatch patch(literal_offset, Type::kPublicTypeBssEntry, target_dex_file);
+    patch.type_idx_ = target_type_idx;
+    patch.pc_insn_offset_ = pc_insn_offset;
+    return patch;
+  }
+
+  static LinkerPatch PackageTypeBssEntryPatch(size_t literal_offset,
+                                              const DexFile* target_dex_file,
+                                              uint32_t pc_insn_offset,
+                                              uint32_t target_type_idx) {
+    LinkerPatch patch(literal_offset, Type::kPackageTypeBssEntry, target_dex_file);
+    patch.type_idx_ = target_type_idx;
+    patch.pc_insn_offset_ = pc_insn_offset;
+    return patch;
+  }
+
   static LinkerPatch RelativeStringPatch(size_t literal_offset,
                                          const DexFile* target_dex_file,
                                          uint32_t pc_insn_offset,
@@ -192,13 +214,17 @@
 
   const DexFile* TargetTypeDexFile() const {
     DCHECK(patch_type_ == Type::kTypeRelative ||
-           patch_type_ == Type::kTypeBssEntry);
+           patch_type_ == Type::kTypeBssEntry ||
+           patch_type_ == Type::kPublicTypeBssEntry ||
+           patch_type_ == Type::kPackageTypeBssEntry);
     return target_dex_file_;
   }
 
   dex::TypeIndex TargetTypeIndex() const {
     DCHECK(patch_type_ == Type::kTypeRelative ||
-           patch_type_ == Type::kTypeBssEntry);
+           patch_type_ == Type::kTypeBssEntry ||
+           patch_type_ == Type::kPublicTypeBssEntry ||
+           patch_type_ == Type::kPackageTypeBssEntry);
     return dex::TypeIndex(type_idx_);
   }
 
@@ -221,6 +247,8 @@
            patch_type_ == Type::kMethodBssEntry ||
            patch_type_ == Type::kTypeRelative ||
            patch_type_ == Type::kTypeBssEntry ||
+           patch_type_ == Type::kPublicTypeBssEntry ||
+           patch_type_ == Type::kPackageTypeBssEntry ||
            patch_type_ == Type::kStringRelative ||
            patch_type_ == Type::kStringBssEntry);
     return pc_insn_offset_;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 3ff83b4..a5d2060 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -327,8 +327,13 @@
       DCHECK(IsSameDexFile(cls_->GetDexFile(), arm64_codegen->GetGraph()->GetDexFile()));
       dex::TypeIndex type_index = cls_->GetTypeIndex();
       __ Mov(calling_convention.GetRegisterAt(0).W(), type_index.index_);
-      arm64_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
-      CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
+      if (cls_->NeedsAccessCheck()) {
+        CheckEntrypointTypes<kQuickResolveTypeAndVerifyAccess, void*, uint32_t>();
+        arm64_codegen->InvokeRuntime(kQuickResolveTypeAndVerifyAccess, instruction_, dex_pc, this);
+      } else {
+        CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
+        arm64_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
+      }
       // If we also must_do_clinit, the resolved type is now in the correct register.
     } else {
       DCHECK(must_do_clinit);
@@ -942,6 +947,8 @@
       method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
+      public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
+      package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
@@ -4733,10 +4740,26 @@
 }
 
 vixl::aarch64::Label* CodeGeneratorARM64::NewBssEntryTypePatch(
-    const DexFile& dex_file,
-    dex::TypeIndex type_index,
+    HLoadClass* load_class,
     vixl::aarch64::Label* adrp_label) {
-  return NewPcRelativePatch(&dex_file, type_index.index_, adrp_label, &type_bss_entry_patches_);
+  const DexFile& dex_file = load_class->GetDexFile();
+  dex::TypeIndex type_index = load_class->GetTypeIndex();
+  ArenaDeque<PcRelativePatchInfo>* patches = nullptr;
+  switch (load_class->GetLoadKind()) {
+    case HLoadClass::LoadKind::kBssEntry:
+      patches = &type_bss_entry_patches_;
+      break;
+    case HLoadClass::LoadKind::kBssEntryPublic:
+      patches = &public_type_bss_entry_patches_;
+      break;
+    case HLoadClass::LoadKind::kBssEntryPackage:
+      patches = &package_type_bss_entry_patches_;
+      break;
+    default:
+      LOG(FATAL) << "Unexpected load kind: " << load_class->GetLoadKind();
+      UNREACHABLE();
+  }
+  return NewPcRelativePatch(&dex_file, type_index.index_, adrp_label, patches);
 }
 
 vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageStringPatch(
@@ -4916,6 +4939,8 @@
       method_bss_entry_patches_.size() +
       boot_image_type_patches_.size() +
       type_bss_entry_patches_.size() +
+      public_type_bss_entry_patches_.size() +
+      package_type_bss_entry_patches_.size() +
       boot_image_string_patches_.size() +
       string_bss_entry_patches_.size() +
       boot_image_other_patches_.size() +
@@ -4945,6 +4970,10 @@
       method_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
       type_bss_entry_patches_, linker_patches);
+  EmitPcRelativeLinkerPatches<linker::LinkerPatch::PublicTypeBssEntryPatch>(
+      public_type_bss_entry_patches_, linker_patches);
+  EmitPcRelativeLinkerPatches<linker::LinkerPatch::PackageTypeBssEntryPatch>(
+      package_type_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
       string_bss_entry_patches_, linker_patches);
   for (const PatchInfo<vixl::aarch64::Label>& info : call_entrypoint_patches_) {
@@ -5063,6 +5092,8 @@
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
+    case HLoadClass::LoadKind::kBssEntryPublic:
+    case HLoadClass::LoadKind::kBssEntryPackage:
       DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kJitBootImageAddress:
@@ -5086,7 +5117,9 @@
     DCHECK(calling_convention.GetRegisterAt(0).Is(vixl::aarch64::x0));
     return;
   }
-  DCHECK(!cls->NeedsAccessCheck());
+  DCHECK_EQ(cls->NeedsAccessCheck(),
+            load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+                load_kind == HLoadClass::LoadKind::kBssEntryPackage);
 
   const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
@@ -5120,7 +5153,9 @@
     codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ __LINE__);
     return;
   }
-  DCHECK(!cls->NeedsAccessCheck());
+  DCHECK_EQ(cls->NeedsAccessCheck(),
+            load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+                load_kind == HLoadClass::LoadKind::kBssEntryPackage);
 
   Location out_loc = cls->GetLocations()->Out();
   Register out = OutputRegister(cls);
@@ -5170,16 +5205,15 @@
       codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X());
       break;
     }
-    case HLoadClass::LoadKind::kBssEntry: {
+    case HLoadClass::LoadKind::kBssEntry:
+    case HLoadClass::LoadKind::kBssEntryPublic:
+    case HLoadClass::LoadKind::kBssEntryPackage: {
       // Add ADRP with its PC-relative Class .bss entry patch.
-      const DexFile& dex_file = cls->GetDexFile();
-      dex::TypeIndex type_index = cls->GetTypeIndex();
       vixl::aarch64::Register temp = XRegisterFrom(out_loc);
-      vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index);
+      vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(cls);
       codegen_->EmitAdrpPlaceholder(adrp_label, temp);
       // Add LDR with its PC-relative Class .bss entry patch.
-      vixl::aarch64::Label* ldr_label =
-          codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label);
+      vixl::aarch64::Label* ldr_label = codegen_->NewBssEntryTypePatch(cls, adrp_label);
       // /* GcRoot<mirror::Class> */ out = *(base_address + offset)  /* PC-relative */
       // All aligned loads are implicitly atomic consume operations on ARM64.
       codegen_->GenerateGcRootFieldLoad(cls,
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 04b2c54..447c0b5 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -736,8 +736,7 @@
   // to be bound before the instruction. The instruction will be either the
   // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing
   // to the associated ADRP patch label).
-  vixl::aarch64::Label* NewBssEntryTypePatch(const DexFile& dex_file,
-                                             dex::TypeIndex type_index,
+  vixl::aarch64::Label* NewBssEntryTypePatch(HLoadClass* load_class,
                                              vixl::aarch64::Label* adrp_label = nullptr);
 
   // Add a new boot image string patch for an instruction and return the label
@@ -1048,6 +1047,10 @@
   ArenaDeque<PcRelativePatchInfo> boot_image_type_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_;
+  // PC-relative public type patch info for kBssEntryPublic.
+  ArenaDeque<PcRelativePatchInfo> public_type_bss_entry_patches_;
+  // PC-relative package type patch info for kBssEntryPackage.
+  ArenaDeque<PcRelativePatchInfo> package_type_bss_entry_patches_;
   // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<PcRelativePatchInfo> boot_image_string_patches_;
   // PC-relative String patch info for kBssEntry.
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 9b1f5ab..9aefdab 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -530,8 +530,13 @@
       DCHECK(IsSameDexFile(cls_->GetDexFile(), arm_codegen->GetGraph()->GetDexFile()));
       dex::TypeIndex type_index = cls_->GetTypeIndex();
       __ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
-      arm_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
-      CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
+      if (cls_->NeedsAccessCheck()) {
+        CheckEntrypointTypes<kQuickResolveTypeAndVerifyAccess, void*, uint32_t>();
+        arm_codegen->InvokeRuntime(kQuickResolveTypeAndVerifyAccess, instruction_, dex_pc, this);
+      } else {
+        CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
+        arm_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
+      }
       // If we also must_do_clinit, the resolved type is now in the correct register.
     } else {
       DCHECK(must_do_clinit);
@@ -1863,6 +1868,8 @@
       method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
+      public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
+      package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
@@ -7299,6 +7306,8 @@
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
+    case HLoadClass::LoadKind::kBssEntryPublic:
+    case HLoadClass::LoadKind::kBssEntryPackage:
       DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kJitBootImageAddress:
@@ -7322,7 +7331,9 @@
     DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
     return;
   }
-  DCHECK(!cls->NeedsAccessCheck());
+  DCHECK_EQ(cls->NeedsAccessCheck(),
+            load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+                load_kind == HLoadClass::LoadKind::kBssEntryPackage);
 
   const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
@@ -7356,7 +7367,9 @@
     codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 15);
     return;
   }
-  DCHECK(!cls->NeedsAccessCheck());
+  DCHECK_EQ(cls->NeedsAccessCheck(),
+            load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+                load_kind == HLoadClass::LoadKind::kBssEntryPackage);
 
   LocationSummary* locations = cls->GetLocations();
   Location out_loc = locations->Out();
@@ -7396,9 +7409,10 @@
       __ Ldr(out, MemOperand(out, /* offset= */ 0));
       break;
     }
-    case HLoadClass::LoadKind::kBssEntry: {
-      CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
-          codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
+    case HLoadClass::LoadKind::kBssEntry:
+    case HLoadClass::LoadKind::kBssEntryPublic:
+    case HLoadClass::LoadKind::kBssEntryPackage: {
+      CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls);
       codegen_->EmitMovwMovtPlaceholder(labels, out);
       // All aligned loads are implicitly atomic consume operations on ARM.
       codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset= */ 0, read_barrier_option);
@@ -9247,7 +9261,24 @@
 }
 
 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch(
-    const DexFile& dex_file, dex::TypeIndex type_index) {
+    HLoadClass* load_class) {
+  const DexFile& dex_file = load_class->GetDexFile();
+  dex::TypeIndex type_index = load_class->GetTypeIndex();
+  ArenaDeque<PcRelativePatchInfo>* patches = nullptr;
+  switch (load_class->GetLoadKind()) {
+    case HLoadClass::LoadKind::kBssEntry:
+      patches = &type_bss_entry_patches_;
+      break;
+    case HLoadClass::LoadKind::kBssEntryPublic:
+      patches = &public_type_bss_entry_patches_;
+      break;
+    case HLoadClass::LoadKind::kBssEntryPackage:
+      patches = &package_type_bss_entry_patches_;
+      break;
+    default:
+      LOG(FATAL) << "Unexpected load kind: " << load_class->GetLoadKind();
+      UNREACHABLE();
+  }
   return NewPcRelativePatch(&dex_file, type_index.index_, &type_bss_entry_patches_);
 }
 
@@ -9397,6 +9428,8 @@
       /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * boot_image_type_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() +
+      /* MOVW+MOVT for each entry */ 2u * public_type_bss_entry_patches_.size() +
+      /* MOVW+MOVT for each entry */ 2u * package_type_bss_entry_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * boot_image_string_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * boot_image_other_patches_.size() +
@@ -9426,6 +9459,10 @@
       method_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
       type_bss_entry_patches_, linker_patches);
+  EmitPcRelativeLinkerPatches<linker::LinkerPatch::PublicTypeBssEntryPatch>(
+      public_type_bss_entry_patches_, linker_patches);
+  EmitPcRelativeLinkerPatches<linker::LinkerPatch::PackageTypeBssEntryPatch>(
+      package_type_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
       string_bss_entry_patches_, linker_patches);
   for (const PatchInfo<vixl32::Label>& info : call_entrypoint_patches_) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index f9b7c0d..0453d20 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -615,7 +615,7 @@
   PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method);
   PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method);
   PcRelativePatchInfo* NewBootImageTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
-  PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file, dex::TypeIndex type_index);
+  PcRelativePatchInfo* NewTypeBssEntryPatch(HLoadClass* load_class);
   PcRelativePatchInfo* NewBootImageStringPatch(const DexFile& dex_file,
                                                dex::StringIndex string_index);
   PcRelativePatchInfo* NewStringBssEntryPatch(const DexFile& dex_file,
@@ -924,6 +924,10 @@
   ArenaDeque<PcRelativePatchInfo> boot_image_type_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_;
+  // PC-relative public type patch info for kBssEntryPublic.
+  ArenaDeque<PcRelativePatchInfo> public_type_bss_entry_patches_;
+  // PC-relative package type patch info for kBssEntryPackage.
+  ArenaDeque<PcRelativePatchInfo> package_type_bss_entry_patches_;
   // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<PcRelativePatchInfo> boot_image_string_patches_;
   // PC-relative String patch info for kBssEntry.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 303c35e..86e6b95 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -291,8 +291,13 @@
       DCHECK(IsSameDexFile(cls_->GetDexFile(), x86_codegen->GetGraph()->GetDexFile()));
       dex::TypeIndex type_index = cls_->GetTypeIndex();
       __ movl(calling_convention.GetRegisterAt(0), Immediate(type_index.index_));
-      x86_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
-      CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
+      if (cls_->NeedsAccessCheck()) {
+        CheckEntrypointTypes<kQuickResolveTypeAndVerifyAccess, void*, uint32_t>();
+        x86_codegen->InvokeRuntime(kQuickResolveTypeAndVerifyAccess, instruction_, dex_pc, this);
+      } else {
+        CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
+        x86_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
+      }
       // If we also must_do_clinit, the resolved type is now in the correct register.
     } else {
       DCHECK(must_do_clinit);
@@ -1056,6 +1061,8 @@
       method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
+      public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
+      package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
@@ -5362,9 +5369,24 @@
 Label* CodeGeneratorX86::NewTypeBssEntryPatch(HLoadClass* load_class) {
   HX86ComputeBaseMethodAddress* method_address =
       load_class->InputAt(0)->AsX86ComputeBaseMethodAddress();
-  type_bss_entry_patches_.emplace_back(
+  ArenaDeque<X86PcRelativePatchInfo>* patches = nullptr;
+  switch (load_class->GetLoadKind()) {
+    case HLoadClass::LoadKind::kBssEntry:
+      patches = &type_bss_entry_patches_;
+      break;
+    case HLoadClass::LoadKind::kBssEntryPublic:
+      patches = &public_type_bss_entry_patches_;
+      break;
+    case HLoadClass::LoadKind::kBssEntryPackage:
+      patches = &package_type_bss_entry_patches_;
+      break;
+    default:
+      LOG(FATAL) << "Unexpected load kind: " << load_class->GetLoadKind();
+      UNREACHABLE();
+  }
+  patches->emplace_back(
       method_address, &load_class->GetDexFile(), load_class->GetTypeIndex().index_);
-  return &type_bss_entry_patches_.back().label;
+  return &patches->back().label;
 }
 
 void CodeGeneratorX86::RecordBootImageStringPatch(HLoadString* load_string) {
@@ -5469,6 +5491,8 @@
       method_bss_entry_patches_.size() +
       boot_image_type_patches_.size() +
       type_bss_entry_patches_.size() +
+      public_type_bss_entry_patches_.size() +
+      package_type_bss_entry_patches_.size() +
       boot_image_string_patches_.size() +
       string_bss_entry_patches_.size() +
       boot_image_other_patches_.size();
@@ -5496,6 +5520,10 @@
       method_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
       type_bss_entry_patches_, linker_patches);
+  EmitPcRelativeLinkerPatches<linker::LinkerPatch::PublicTypeBssEntryPatch>(
+      public_type_bss_entry_patches_, linker_patches);
+  EmitPcRelativeLinkerPatches<linker::LinkerPatch::PackageTypeBssEntryPatch>(
+      package_type_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
       string_bss_entry_patches_, linker_patches);
   DCHECK_EQ(size, linker_patches->size());
@@ -6933,6 +6961,8 @@
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
+    case HLoadClass::LoadKind::kBssEntryPublic:
+    case HLoadClass::LoadKind::kBssEntryPackage:
       DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kJitBootImageAddress:
@@ -6956,7 +6986,9 @@
     DCHECK_EQ(calling_convention.GetRegisterAt(0), EAX);
     return;
   }
-  DCHECK(!cls->NeedsAccessCheck());
+  DCHECK_EQ(cls->NeedsAccessCheck(),
+            load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+                load_kind == HLoadClass::LoadKind::kBssEntryPackage);
 
   const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
@@ -6967,14 +6999,11 @@
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
 
-  if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
-      load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative ||
-      load_kind == HLoadClass::LoadKind::kBootImageRelRo ||
-      load_kind == HLoadClass::LoadKind::kBssEntry) {
+  if (load_kind == HLoadClass::LoadKind::kReferrersClass || cls->HasPcRelativeLoadKind()) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
   locations->SetOut(Location::RequiresRegister());
-  if (load_kind == HLoadClass::LoadKind::kBssEntry) {
+  if (call_kind == LocationSummary::kCallOnSlowPath && cls->HasPcRelativeLoadKind()) {
     if (!kUseReadBarrier || kUseBakerReadBarrier) {
       // Rely on the type resolution and/or initialization to save everything.
       locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
@@ -7002,7 +7031,9 @@
     codegen_->GenerateLoadClassRuntimeCall(cls);
     return;
   }
-  DCHECK(!cls->NeedsAccessCheck());
+  DCHECK_EQ(cls->NeedsAccessCheck(),
+            load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+                load_kind == HLoadClass::LoadKind::kBssEntryPackage);
 
   LocationSummary* locations = cls->GetLocations();
   Location out_loc = locations->Out();
@@ -7043,7 +7074,9 @@
                                           codegen_->GetBootImageOffset(cls));
       break;
     }
-    case HLoadClass::LoadKind::kBssEntry: {
+    case HLoadClass::LoadKind::kBssEntry:
+    case HLoadClass::LoadKind::kBssEntryPublic:
+    case HLoadClass::LoadKind::kBssEntryPackage: {
       Register method_address = locations->InAt(0).AsRegister<Register>();
       Address address(method_address, CodeGeneratorX86::kPlaceholder32BitOffset);
       Label* fixup_label = codegen_->NewTypeBssEntryPatch(cls);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index a573e84..b0575ba 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -715,6 +715,10 @@
   ArenaDeque<X86PcRelativePatchInfo> boot_image_type_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<X86PcRelativePatchInfo> type_bss_entry_patches_;
+  // PC-relative public type patch info for kBssEntryPublic.
+  ArenaDeque<X86PcRelativePatchInfo> public_type_bss_entry_patches_;
+  // PC-relative package type patch info for kBssEntryPackage.
+  ArenaDeque<X86PcRelativePatchInfo> package_type_bss_entry_patches_;
   // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<X86PcRelativePatchInfo> boot_image_string_patches_;
   // PC-relative String patch info for kBssEntry.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index f18fde0..202b58b 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -272,8 +272,13 @@
       DCHECK(IsSameDexFile(cls_->GetDexFile(), x86_64_codegen->GetGraph()->GetDexFile()));
       dex::TypeIndex type_index = cls_->GetTypeIndex();
       __ movl(CpuRegister(RAX), Immediate(type_index.index_));
-      x86_64_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
-      CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
+      if (cls_->NeedsAccessCheck()) {
+        CheckEntrypointTypes<kQuickResolveTypeAndVerifyAccess, void*, uint32_t>();
+        x86_64_codegen->InvokeRuntime(kQuickResolveTypeAndVerifyAccess, instruction_, dex_pc, this);
+      } else {
+        CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
+        x86_64_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
+      }
       // If we also must_do_clinit, the resolved type is now in the correct register.
     } else {
       DCHECK(must_do_clinit);
@@ -1162,9 +1167,23 @@
 }
 
 Label* CodeGeneratorX86_64::NewTypeBssEntryPatch(HLoadClass* load_class) {
-  type_bss_entry_patches_.emplace_back(
-      &load_class->GetDexFile(), load_class->GetTypeIndex().index_);
-  return &type_bss_entry_patches_.back().label;
+  ArenaDeque<PatchInfo<Label>>* patches = nullptr;
+  switch (load_class->GetLoadKind()) {
+    case HLoadClass::LoadKind::kBssEntry:
+      patches = &type_bss_entry_patches_;
+      break;
+    case HLoadClass::LoadKind::kBssEntryPublic:
+      patches = &public_type_bss_entry_patches_;
+      break;
+    case HLoadClass::LoadKind::kBssEntryPackage:
+      patches = &package_type_bss_entry_patches_;
+      break;
+    default:
+      LOG(FATAL) << "Unexpected load kind: " << load_class->GetLoadKind();
+      UNREACHABLE();
+  }
+  patches->emplace_back(&load_class->GetDexFile(), load_class->GetTypeIndex().index_);
+  return &patches->back().label;
 }
 
 void CodeGeneratorX86_64::RecordBootImageStringPatch(HLoadString* load_string) {
@@ -1249,6 +1268,8 @@
       method_bss_entry_patches_.size() +
       boot_image_type_patches_.size() +
       type_bss_entry_patches_.size() +
+      public_type_bss_entry_patches_.size() +
+      package_type_bss_entry_patches_.size() +
       boot_image_string_patches_.size() +
       string_bss_entry_patches_.size() +
       boot_image_other_patches_.size();
@@ -1276,6 +1297,10 @@
       method_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
       type_bss_entry_patches_, linker_patches);
+  EmitPcRelativeLinkerPatches<linker::LinkerPatch::PublicTypeBssEntryPatch>(
+      public_type_bss_entry_patches_, linker_patches);
+  EmitPcRelativeLinkerPatches<linker::LinkerPatch::PackageTypeBssEntryPatch>(
+      package_type_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
       string_bss_entry_patches_, linker_patches);
   DCHECK_EQ(size, linker_patches->size());
@@ -1370,6 +1395,8 @@
         method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
         boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
         type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
+        public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
+        package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
         boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
         string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
         boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
@@ -6092,6 +6119,8 @@
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
+    case HLoadClass::LoadKind::kBssEntryPublic:
+    case HLoadClass::LoadKind::kBssEntryPackage:
       DCHECK(!GetCompilerOptions().IsJitCompiler());
       break;
     case HLoadClass::LoadKind::kJitBootImageAddress:
@@ -6114,7 +6143,9 @@
         Location::RegisterLocation(RAX));
     return;
   }
-  DCHECK(!cls->NeedsAccessCheck());
+  DCHECK_EQ(cls->NeedsAccessCheck(),
+            load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+                load_kind == HLoadClass::LoadKind::kBssEntryPackage);
 
   const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
@@ -6157,7 +6188,9 @@
     codegen_->GenerateLoadClassRuntimeCall(cls);
     return;
   }
-  DCHECK(!cls->NeedsAccessCheck());
+  DCHECK_EQ(cls->NeedsAccessCheck(),
+            load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+                load_kind == HLoadClass::LoadKind::kBssEntryPackage);
 
   LocationSummary* locations = cls->GetLocations();
   Location out_loc = locations->Out();
@@ -6196,7 +6229,9 @@
       codegen_->RecordBootImageRelRoPatch(codegen_->GetBootImageOffset(cls));
       break;
     }
-    case HLoadClass::LoadKind::kBssEntry: {
+    case HLoadClass::LoadKind::kBssEntry:
+    case HLoadClass::LoadKind::kBssEntryPublic:
+    case HLoadClass::LoadKind::kBssEntryPackage: {
       Address address = Address::Absolute(CodeGeneratorX86_64::kPlaceholder32BitOffset,
                                           /* no_rip= */ false);
       Label* fixup_label = codegen_->NewTypeBssEntryPatch(cls);
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index dc18a24..81988b4 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -668,6 +668,10 @@
   ArenaDeque<PatchInfo<Label>> boot_image_type_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<PatchInfo<Label>> type_bss_entry_patches_;
+  // PC-relative public type patch info for kBssEntryPublic.
+  ArenaDeque<PatchInfo<Label>> public_type_bss_entry_patches_;
+  // PC-relative package type patch info for kBssEntryPackage.
+  ArenaDeque<PatchInfo<Label>> package_type_bss_entry_patches_;
   // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<PatchInfo<Label>> boot_image_string_patches_;
   // PC-relative String patch info for kBssEntry.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index d7a66f1..b674937 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -6391,6 +6391,21 @@
     // Used for classes outside boot image referenced by AOT-compiled app and boot image code.
     kBssEntry,
 
+    // Load from an entry for public class in the .bss section using a PC-relative load.
+    // Used for classes that were unresolved during AOT-compilation outside the literal
+    // package of the compiling class. Such classes are accessible only if they are public
+    // and the .bss entry shall therefore be filled only if the resolved class is public.
+    kBssEntryPublic,
+
+    // Load from an entry for package class in the .bss section using a PC-relative load.
+    // Used for classes that were unresolved during AOT-compilation but within the literal
+    // package of the compiling class. Such classes are accessible if they are public or
+    // in the same package which, given the literal package match, requires only matching
+    // defining class loader and the .bss entry shall therefore be filled only if at least
+    // one of those conditions holds. Note that all code in an oat file belongs to classes
+    // with the same defining class loader.
+    kBssEntryPackage,
+
     // Use a known boot image Class* address, embedded in the code by the codegen.
     // Used for boot image classes referenced by apps in JIT-compiled code.
     kJitBootImageAddress,
@@ -6443,7 +6458,9 @@
   bool HasPcRelativeLoadKind() const {
     return GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
            GetLoadKind() == LoadKind::kBootImageRelRo ||
-           GetLoadKind() == LoadKind::kBssEntry;
+           GetLoadKind() == LoadKind::kBssEntry ||
+           GetLoadKind() == LoadKind::kBssEntryPublic ||
+           GetLoadKind() == LoadKind::kBssEntryPackage;
   }
 
   bool CanBeMoved() const override { return true; }
@@ -6459,9 +6476,6 @@
   }
 
   void SetMustGenerateClinitCheck(bool generate_clinit_check) {
-    // The entrypoint the code generator is going to call does not do
-    // clinit of the class.
-    DCHECK(!NeedsAccessCheck());
     SetPackedFlag<kFlagGenerateClInitCheck>(generate_clinit_check);
   }
 
@@ -6514,9 +6528,14 @@
 
   bool MustResolveTypeOnSlowPath() const {
     // Check that this instruction has a slow path.
-    DCHECK(GetLoadKind() != LoadKind::kRuntimeCall);  // kRuntimeCall calls on main path.
-    DCHECK(GetLoadKind() == LoadKind::kBssEntry || MustGenerateClinitCheck());
-    return GetLoadKind() == LoadKind::kBssEntry;
+    LoadKind load_kind = GetLoadKind();
+    DCHECK(load_kind != LoadKind::kRuntimeCall);  // kRuntimeCall calls on main path.
+    bool must_resolve_type_on_slow_path =
+       load_kind == LoadKind::kBssEntry ||
+       load_kind == LoadKind::kBssEntryPublic ||
+       load_kind == LoadKind::kBssEntryPackage;
+    DCHECK(must_resolve_type_on_slow_path || MustGenerateClinitCheck());
+    return must_resolve_type_on_slow_path;
   }
 
   void MarkInBootImage() {
@@ -6558,6 +6577,8 @@
     return load_kind == LoadKind::kReferrersClass ||
         load_kind == LoadKind::kBootImageLinkTimePcRelative ||
         load_kind == LoadKind::kBssEntry ||
+        load_kind == LoadKind::kBssEntryPublic ||
+        load_kind == LoadKind::kBssEntryPackage ||
         load_kind == LoadKind::kRuntimeCall;
   }
 
@@ -6565,14 +6586,14 @@
 
   // The special input is the HCurrentMethod for kRuntimeCall or kReferrersClass.
   // For other load kinds it's empty or possibly some architecture-specific instruction
-  // for PC-relative loads, i.e. kBssEntry or kBootImageLinkTimePcRelative.
+  // for PC-relative loads, i.e. kBssEntry* or kBootImageLinkTimePcRelative.
   HUserRecord<HInstruction*> special_input_;
 
   // A type index and dex file where the class can be accessed. The dex file can be:
   // - The compiling method's dex file if the class is defined there too.
   // - The compiling method's dex file if the class is referenced there.
   // - The dex file where the class is defined. When the load kind can only be
-  //   kBssEntry or kRuntimeCall, we cannot emit code for this `HLoadClass`.
+  //   kBssEntry* or kRuntimeCall, we cannot emit code for this `HLoadClass`.
   const dex::TypeIndex type_index_;
   const DexFile& dex_file_;
 
@@ -6601,6 +6622,8 @@
   DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
          GetLoadKind() == LoadKind::kBootImageRelRo ||
          GetLoadKind() == LoadKind::kBssEntry ||
+         GetLoadKind() == LoadKind::kBssEntryPublic ||
+         GetLoadKind() == LoadKind::kBssEntryPackage ||
          GetLoadKind() == LoadKind::kJitBootImageAddress) << GetLoadKind();
   DCHECK(special_input_.GetInstruction() == nullptr);
   special_input_ = HUserRecord<HInstruction*>(special_input);
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 8c4615d..d5edc3d 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -166,7 +166,6 @@
     }
   } else if (can_merge_with_load_class &&
              load_class->GetLoadKind() != HLoadClass::LoadKind::kRuntimeCall) {
-    DCHECK(!load_class->NeedsAccessCheck());
     // Pass the initialization duty to the `HLoadClass` instruction,
     // and remove the instruction from the graph.
     DCHECK(load_class->HasEnvironment());
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 393369d..f570c60 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -158,24 +158,55 @@
          load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass)
       << load_class->GetLoadKind();
   DCHECK(!load_class->IsInBootImage()) << "HLoadClass should not be optimized before sharpening.";
+  const DexFile& dex_file = load_class->GetDexFile();
+  dex::TypeIndex type_index = load_class->GetTypeIndex();
+  const CompilerOptions& compiler_options = codegen->GetCompilerOptions();
 
-  HLoadClass::LoadKind load_kind = load_class->GetLoadKind();
+  bool is_in_boot_image = false;
+  HLoadClass::LoadKind desired_load_kind = HLoadClass::LoadKind::kInvalid;
 
-  if (load_class->NeedsAccessCheck()) {
-    // We need to call the runtime anyway, so we simply get the class as that call's return value.
-  } else if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
+  if (load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) {
+    DCHECK(!load_class->NeedsAccessCheck());
     // Loading from the ArtMethod* is the most efficient retrieval in code size.
     // TODO: This may not actually be true for all architectures and
     // locations of target classes. The additional register pressure
     // for using the ArtMethod* should be considered.
+    desired_load_kind = HLoadClass::LoadKind::kReferrersClass;
+  } else if (load_class->NeedsAccessCheck()) {
+    DCHECK_EQ(load_class->GetLoadKind(), HLoadClass::LoadKind::kRuntimeCall);
+    if (klass != nullptr) {
+      // Resolved class that needs access check must be really inaccessible
+      // and the access check is bound to fail. Just emit the runtime call.
+      desired_load_kind = HLoadClass::LoadKind::kRuntimeCall;
+    } else if (compiler_options.IsJitCompiler()) {
+      // Unresolved class while JITting means that either we never hit this
+      // instruction or it failed. Either way, just emit the runtime call.
+      // (Though we could consider emitting Deoptimize instead and
+      // recompile if the instruction succeeds in interpreter.)
+      desired_load_kind = HLoadClass::LoadKind::kRuntimeCall;
+    } else {
+      // For AOT, check if the class is in the same literal package as the
+      // compiling class and pick an appropriate .bss entry.
+      auto package_length = [](const char* descriptor) {
+        const char* slash_pos = strrchr(descriptor, '/');
+        return (slash_pos != nullptr) ? static_cast<size_t>(slash_pos - descriptor) : 0u;
+      };
+      const char* klass_descriptor = dex_file.StringByTypeIdx(type_index);
+      const uint32_t klass_package_length = package_length(klass_descriptor);
+      const DexFile* referrer_dex_file = dex_compilation_unit.GetDexFile();
+      const dex::TypeIndex referrer_type_index =
+          referrer_dex_file->GetClassDef(dex_compilation_unit.GetClassDefIndex()).class_idx_;
+      const char* referrer_descriptor = referrer_dex_file->StringByTypeIdx(referrer_type_index);
+      const uint32_t referrer_package_length = package_length(referrer_descriptor);
+      bool same_package =
+          (referrer_package_length == klass_package_length) &&
+          memcmp(referrer_descriptor, klass_descriptor, referrer_package_length) == 0;
+      desired_load_kind = same_package
+          ? HLoadClass::LoadKind::kBssEntryPackage
+          : HLoadClass::LoadKind::kBssEntryPublic;
+    }
   } else {
-    const DexFile& dex_file = load_class->GetDexFile();
-    dex::TypeIndex type_index = load_class->GetTypeIndex();
-
-    bool is_in_boot_image = false;
-    HLoadClass::LoadKind desired_load_kind = HLoadClass::LoadKind::kInvalid;
     Runtime* runtime = Runtime::Current();
-    const CompilerOptions& compiler_options = codegen->GetCompilerOptions();
     if (compiler_options.IsBootImage() || compiler_options.IsBootImageExtension()) {
       // Compiling boot image or boot image extension. Check if the class is a boot image class.
       DCHECK(!compiler_options.IsJitCompiler());
@@ -227,17 +258,19 @@
         desired_load_kind = HLoadClass::LoadKind::kBssEntry;
       }
     }
-    DCHECK_NE(desired_load_kind, HLoadClass::LoadKind::kInvalid);
-
-    if (is_in_boot_image) {
-      load_class->MarkInBootImage();
-    }
-    load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind);
   }
+  DCHECK_NE(desired_load_kind, HLoadClass::LoadKind::kInvalid);
+
+  if (is_in_boot_image) {
+    load_class->MarkInBootImage();
+  }
+  HLoadClass::LoadKind load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind);
 
   if (!IsSameDexFile(load_class->GetDexFile(), *dex_compilation_unit.GetDexFile())) {
-    if ((load_kind == HLoadClass::LoadKind::kRuntimeCall) ||
-        (load_kind == HLoadClass::LoadKind::kBssEntry)) {
+    if (load_kind == HLoadClass::LoadKind::kRuntimeCall ||
+        load_kind == HLoadClass::LoadKind::kBssEntry ||
+        load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+        load_kind == HLoadClass::LoadKind::kBssEntryPackage) {
       // We actually cannot reference this class, we're forced to bail.
       // We cannot reference this class with Bss, as the entrypoint will lookup the class
       // in the caller's dex file, but that dex file does not reference the class.
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64.cc b/dex2oat/linker/arm64/relative_patcher_arm64.cc
index 2260f66..494391b 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64.cc
@@ -67,6 +67,8 @@
     case LinkerPatch::Type::kMethodBssEntry:
     case LinkerPatch::Type::kTypeRelative:
     case LinkerPatch::Type::kTypeBssEntry:
+    case LinkerPatch::Type::kPublicTypeBssEntry:
+    case LinkerPatch::Type::kPackageTypeBssEntry:
     case LinkerPatch::Type::kStringRelative:
     case LinkerPatch::Type::kStringBssEntry:
       return patch.LiteralOffset() == patch.PcInsnOffset();
@@ -261,6 +263,8 @@
                patch.GetType() == LinkerPatch::Type::kTypeRelative ||
                patch.GetType() == LinkerPatch::Type::kStringRelative ||
                patch.GetType() == LinkerPatch::Type::kTypeBssEntry ||
+               patch.GetType() == LinkerPatch::Type::kPublicTypeBssEntry ||
+               patch.GetType() == LinkerPatch::Type::kPackageTypeBssEntry ||
                patch.GetType() == LinkerPatch::Type::kStringBssEntry) << patch.GetType();
       }
       shift = 0u;  // No shift for ADD.
@@ -269,6 +273,8 @@
       DCHECK(patch.GetType() == LinkerPatch::Type::kDataBimgRelRo ||
              patch.GetType() == LinkerPatch::Type::kMethodBssEntry ||
              patch.GetType() == LinkerPatch::Type::kTypeBssEntry ||
+             patch.GetType() == LinkerPatch::Type::kPublicTypeBssEntry ||
+             patch.GetType() == LinkerPatch::Type::kPackageTypeBssEntry ||
              patch.GetType() == LinkerPatch::Type::kStringBssEntry) << patch.GetType();
       DCHECK_EQ(insn & 0xbfbffc00, 0xb9000000) << std::hex << insn;
     }
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index a49b7d4..73ad9e9 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -351,6 +351,8 @@
   uint32_t class_offsets_offset_;
   uint32_t method_bss_mapping_offset_;
   uint32_t type_bss_mapping_offset_;
+  uint32_t public_type_bss_mapping_offset_;
+  uint32_t package_type_bss_mapping_offset_;
   uint32_t string_bss_mapping_offset_;
 
   // Offset of dex sections that will have different runtime madvise states.
@@ -412,6 +414,8 @@
     bss_method_entry_references_(),
     bss_method_entries_(),
     bss_type_entries_(),
+    bss_public_type_entries_(),
+    bss_package_type_entries_(),
     bss_string_entries_(),
     oat_data_offset_(0u),
     oat_header_(nullptr),
@@ -456,6 +460,8 @@
     size_oat_dex_file_dex_layout_sections_alignment_(0),
     size_oat_dex_file_method_bss_mapping_offset_(0),
     size_oat_dex_file_type_bss_mapping_offset_(0),
+    size_oat_dex_file_public_type_bss_mapping_offset_(0),
+    size_oat_dex_file_package_type_bss_mapping_offset_(0),
     size_oat_dex_file_string_bss_mapping_offset_(0),
     size_oat_lookup_table_alignment_(0),
     size_oat_lookup_table_(0),
@@ -467,6 +473,8 @@
     size_oat_class_method_offsets_(0),
     size_method_bss_mappings_(0u),
     size_type_bss_mappings_(0u),
+    size_public_type_bss_mappings_(0u),
+    size_package_type_bss_mappings_(0u),
     size_string_bss_mappings_(0u),
     relative_patcher_(nullptr),
     profile_compilation_info_(info),
@@ -917,6 +925,18 @@
                           target_type.dex_file->NumTypeIds(),
                           &writer_->bss_type_entry_references_);
           writer_->bss_type_entries_.Overwrite(target_type, /* placeholder */ 0u);
+        } else if (patch.GetType() == LinkerPatch::Type::kPublicTypeBssEntry) {
+          TypeReference target_type(patch.TargetTypeDexFile(), patch.TargetTypeIndex());
+          AddBssReference(target_type,
+                          target_type.dex_file->NumTypeIds(),
+                          &writer_->bss_public_type_entry_references_);
+          writer_->bss_public_type_entries_.Overwrite(target_type, /* placeholder */ 0u);
+        } else if (patch.GetType() == LinkerPatch::Type::kPackageTypeBssEntry) {
+          TypeReference target_type(patch.TargetTypeDexFile(), patch.TargetTypeIndex());
+          AddBssReference(target_type,
+                          target_type.dex_file->NumTypeIds(),
+                          &writer_->bss_package_type_entry_references_);
+          writer_->bss_package_type_entries_.Overwrite(target_type, /* placeholder */ 0u);
         } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) {
           StringReference target_string(patch.TargetStringDexFile(), patch.TargetStringIndex());
           AddBssReference(target_string,
@@ -1839,6 +1859,26 @@
                                                                    target_offset);
               break;
             }
+            case LinkerPatch::Type::kPublicTypeBssEntry: {
+              TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex());
+              uint32_t target_offset =
+                  writer_->bss_start_ + writer_->bss_public_type_entries_.Get(ref);
+              writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+                                                                   patch,
+                                                                   offset_ + literal_offset,
+                                                                   target_offset);
+              break;
+            }
+            case LinkerPatch::Type::kPackageTypeBssEntry: {
+              TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex());
+              uint32_t target_offset =
+                  writer_->bss_start_ + writer_->bss_package_type_entries_.Get(ref);
+              writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+                                                                   patch,
+                                                                   offset_ + literal_offset,
+                                                                   target_offset);
+              break;
+            }
             case LinkerPatch::Type::kMethodRelative: {
               uint32_t target_offset = GetTargetMethodOffset(GetTargetMethod(patch));
               writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
@@ -2125,9 +2165,22 @@
   return IndexBssMapping::ComputeSize(number_of_entries);
 }
 
+static size_t CalculateIndexBssMappingSize(
+    const DexFile* dex_file,
+    const BitVector& type_indexes,
+    const SafeMap<TypeReference, size_t, TypeReferenceValueComparator>& bss_entries) {
+  return CalculateIndexBssMappingSize(
+      dex_file->NumTypeIds(),
+      sizeof(GcRoot<mirror::Class>),
+      type_indexes,
+      [=](uint32_t index) { return bss_entries.Get({dex_file, dex::TypeIndex(index)}); });
+}
+
 size_t OatWriter::InitIndexBssMappings(size_t offset) {
   if (bss_method_entry_references_.empty() &&
       bss_type_entry_references_.empty() &&
+      bss_public_type_entry_references_.empty() &&
+      bss_package_type_entry_references_.empty() &&
       bss_string_entry_references_.empty()) {
     return offset;
   }
@@ -2138,6 +2191,8 @@
 
   size_t number_of_method_dex_files = 0u;
   size_t number_of_type_dex_files = 0u;
+  size_t number_of_public_type_dex_files = 0u;
+  size_t number_of_package_type_dex_files = 0u;
   size_t number_of_string_dex_files = 0u;
   PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet());
   for (size_t i = 0, size = dex_files_->size(); i != size; ++i) {
@@ -2161,13 +2216,23 @@
       const BitVector& type_indexes = type_it->second;
       ++number_of_type_dex_files;
       oat_dex_files_[i].type_bss_mapping_offset_ = offset;
-      offset += CalculateIndexBssMappingSize(
-          dex_file->NumTypeIds(),
-          sizeof(GcRoot<mirror::Class>),
-          type_indexes,
-          [=](uint32_t index) {
-            return bss_type_entries_.Get({dex_file, dex::TypeIndex(index)});
-          });
+      offset += CalculateIndexBssMappingSize(dex_file, type_indexes, bss_type_entries_);
+    }
+
+    auto public_type_it = bss_public_type_entry_references_.find(dex_file);
+    if (public_type_it != bss_public_type_entry_references_.end()) {
+      const BitVector& type_indexes = public_type_it->second;
+      ++number_of_public_type_dex_files;
+      oat_dex_files_[i].public_type_bss_mapping_offset_ = offset;
+      offset += CalculateIndexBssMappingSize(dex_file, type_indexes, bss_public_type_entries_);
+    }
+
+    auto package_type_it = bss_package_type_entry_references_.find(dex_file);
+    if (package_type_it != bss_package_type_entry_references_.end()) {
+      const BitVector& type_indexes = package_type_it->second;
+      ++number_of_package_type_dex_files;
+      oat_dex_files_[i].package_type_bss_mapping_offset_ = offset;
+      offset += CalculateIndexBssMappingSize(dex_file, type_indexes, bss_package_type_entries_);
     }
 
     auto string_it = bss_string_entry_references_.find(dex_file);
@@ -2187,6 +2252,8 @@
   // Check that all dex files targeted by bss entries are in `*dex_files_`.
   CHECK_EQ(number_of_method_dex_files, bss_method_entry_references_.size());
   CHECK_EQ(number_of_type_dex_files, bss_type_entry_references_.size());
+  CHECK_EQ(number_of_public_type_dex_files, bss_public_type_entry_references_.size());
+  CHECK_EQ(number_of_package_type_dex_files, bss_package_type_entry_references_.size());
   CHECK_EQ(number_of_string_dex_files, bss_string_entry_references_.size());
   return offset;
 }
@@ -2338,6 +2405,8 @@
   DCHECK_EQ(bss_size_, 0u);
   if (bss_method_entries_.empty() &&
       bss_type_entries_.empty() &&
+      bss_public_type_entries_.empty() &&
+      bss_package_type_entries_.empty() &&
       bss_string_entries_.empty()) {
     // Nothing to put to the .bss section.
     return;
@@ -2361,6 +2430,18 @@
     entry.second = bss_size_;
     bss_size_ += sizeof(GcRoot<mirror::Class>);
   }
+  // Prepare offsets for .bss public Class entries.
+  for (auto& entry : bss_public_type_entries_) {
+    DCHECK_EQ(entry.second, 0u);
+    entry.second = bss_size_;
+    bss_size_ += sizeof(GcRoot<mirror::Class>);
+  }
+  // Prepare offsets for .bss package Class entries.
+  for (auto& entry : bss_package_type_entries_) {
+    DCHECK_EQ(entry.second, 0u);
+    entry.second = bss_size_;
+    bss_size_ += sizeof(GcRoot<mirror::Class>);
+  }
   // Prepare offsets for .bss String entries.
   for (auto& entry : bss_string_entries_) {
     DCHECK_EQ(entry.second, 0u);
@@ -2738,6 +2819,8 @@
     DO_STAT(size_oat_dex_file_dex_layout_sections_alignment_);
     DO_STAT(size_oat_dex_file_method_bss_mapping_offset_);
     DO_STAT(size_oat_dex_file_type_bss_mapping_offset_);
+    DO_STAT(size_oat_dex_file_public_type_bss_mapping_offset_);
+    DO_STAT(size_oat_dex_file_package_type_bss_mapping_offset_);
     DO_STAT(size_oat_dex_file_string_bss_mapping_offset_);
     DO_STAT(size_oat_lookup_table_alignment_);
     DO_STAT(size_oat_lookup_table_);
@@ -2749,6 +2832,8 @@
     DO_STAT(size_oat_class_method_offsets_);
     DO_STAT(size_method_bss_mappings_);
     DO_STAT(size_type_bss_mappings_);
+    DO_STAT(size_public_type_bss_mappings_);
+    DO_STAT(size_package_type_bss_mappings_);
     DO_STAT(size_string_bss_mappings_);
     #undef DO_STAT
 
@@ -2914,12 +2999,27 @@
   return mappings_size;
 }
 
+size_t WriteIndexBssMapping(
+    OutputStream* out,
+    const DexFile* dex_file,
+    const BitVector& type_indexes,
+    const SafeMap<TypeReference, size_t, TypeReferenceValueComparator>& bss_entries) {
+  return WriteIndexBssMapping(
+      out,
+      dex_file->NumTypeIds(),
+      sizeof(GcRoot<mirror::Class>),
+      type_indexes,
+      [=](uint32_t index) { return bss_entries.Get({dex_file, dex::TypeIndex(index)}); });
+}
+
 size_t OatWriter::WriteIndexBssMappings(OutputStream* out,
                                         size_t file_offset,
                                         size_t relative_offset) {
   TimingLogger::ScopedTiming split("WriteMethodBssMappings", timings_);
   if (bss_method_entry_references_.empty() &&
       bss_type_entry_references_.empty() &&
+      bss_public_type_entry_references_.empty() &&
+      bss_package_type_entry_references_.empty() &&
       bss_string_entry_references_.empty()) {
     return relative_offset;
   }
@@ -2960,14 +3060,8 @@
       const BitVector& type_indexes = type_it->second;
       DCHECK_EQ(relative_offset, oat_dex_file->type_bss_mapping_offset_);
       DCHECK_OFFSET();
-      size_t type_mappings_size = WriteIndexBssMapping(
-          out,
-          dex_file->NumTypeIds(),
-          sizeof(GcRoot<mirror::Class>),
-          type_indexes,
-          [=](uint32_t index) {
-            return bss_type_entries_.Get({dex_file, dex::TypeIndex(index)});
-          });
+      size_t type_mappings_size =
+          WriteIndexBssMapping(out, dex_file, type_indexes, bss_type_entries_);
       if (type_mappings_size == 0u) {
         return 0u;
       }
@@ -2977,6 +3071,38 @@
       DCHECK_EQ(0u, oat_dex_file->type_bss_mapping_offset_);
     }
 
+    auto public_type_it = bss_public_type_entry_references_.find(dex_file);
+    if (public_type_it != bss_public_type_entry_references_.end()) {
+      const BitVector& type_indexes = public_type_it->second;
+      DCHECK_EQ(relative_offset, oat_dex_file->public_type_bss_mapping_offset_);
+      DCHECK_OFFSET();
+      size_t public_type_mappings_size =
+          WriteIndexBssMapping(out, dex_file, type_indexes, bss_public_type_entries_);
+      if (public_type_mappings_size == 0u) {
+        return 0u;
+      }
+      size_public_type_bss_mappings_ += public_type_mappings_size;
+      relative_offset += public_type_mappings_size;
+    } else {
+      DCHECK_EQ(0u, oat_dex_file->public_type_bss_mapping_offset_);
+    }
+
+    auto package_type_it = bss_package_type_entry_references_.find(dex_file);
+    if (package_type_it != bss_package_type_entry_references_.end()) {
+      const BitVector& type_indexes = package_type_it->second;
+      DCHECK_EQ(relative_offset, oat_dex_file->package_type_bss_mapping_offset_);
+      DCHECK_OFFSET();
+      size_t package_type_mappings_size =
+          WriteIndexBssMapping(out, dex_file, type_indexes, bss_package_type_entries_);
+      if (package_type_mappings_size == 0u) {
+        return 0u;
+      }
+      size_package_type_bss_mappings_ += package_type_mappings_size;
+      relative_offset += package_type_mappings_size;
+    } else {
+      DCHECK_EQ(0u, oat_dex_file->package_type_bss_mapping_offset_);
+    }
+
     auto string_it = bss_string_entry_references_.find(dex_file);
     if (string_it != bss_string_entry_references_.end()) {
       const BitVector& string_indexes = string_it->second;
@@ -3918,6 +4044,8 @@
       class_offsets_offset_(0u),
       method_bss_mapping_offset_(0u),
       type_bss_mapping_offset_(0u),
+      public_type_bss_mapping_offset_(0u),
+      package_type_bss_mapping_offset_(0u),
       string_bss_mapping_offset_(0u),
       dex_sections_layout_offset_(0u),
       class_offsets_() {
@@ -3932,6 +4060,8 @@
           + sizeof(lookup_table_offset_)
           + sizeof(method_bss_mapping_offset_)
           + sizeof(type_bss_mapping_offset_)
+          + sizeof(public_type_bss_mapping_offset_)
+          + sizeof(package_type_bss_mapping_offset_)
           + sizeof(string_bss_mapping_offset_)
           + sizeof(dex_sections_layout_offset_);
 }
@@ -3994,6 +4124,21 @@
   }
   oat_writer->size_oat_dex_file_type_bss_mapping_offset_ += sizeof(type_bss_mapping_offset_);
 
+  if (!out->WriteFully(&public_type_bss_mapping_offset_, sizeof(public_type_bss_mapping_offset_))) {
+    PLOG(ERROR) << "Failed to write public type bss mapping offset to " << out->GetLocation();
+    return false;
+  }
+  oat_writer->size_oat_dex_file_public_type_bss_mapping_offset_ +=
+      sizeof(public_type_bss_mapping_offset_);
+
+  if (!out->WriteFully(&package_type_bss_mapping_offset_,
+                       sizeof(package_type_bss_mapping_offset_))) {
+    PLOG(ERROR) << "Failed to write package type bss mapping offset to " << out->GetLocation();
+    return false;
+  }
+  oat_writer->size_oat_dex_file_package_type_bss_mapping_offset_ +=
+      sizeof(package_type_bss_mapping_offset_);
+
   if (!out->WriteFully(&string_bss_mapping_offset_, sizeof(string_bss_mapping_offset_))) {
     PLOG(ERROR) << "Failed to write string bss mapping offset to " << out->GetLocation();
     return false;
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index 8d4f574..2fd7571 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -441,6 +441,12 @@
   // Map for recording references to GcRoot<mirror::Class> entries in .bss.
   SafeMap<const DexFile*, BitVector> bss_type_entry_references_;
 
+  // Map for recording references to public GcRoot<mirror::Class> entries in .bss.
+  SafeMap<const DexFile*, BitVector> bss_public_type_entry_references_;
+
+  // Map for recording references to package GcRoot<mirror::Class> entries in .bss.
+  SafeMap<const DexFile*, BitVector> bss_package_type_entry_references_;
+
   // Map for recording references to GcRoot<mirror::String> entries in .bss.
   SafeMap<const DexFile*, BitVector> bss_string_entry_references_;
 
@@ -454,6 +460,16 @@
   // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`.
   SafeMap<TypeReference, size_t, TypeReferenceValueComparator> bss_type_entries_;
 
+  // Map for allocating public Class entries in .bss. Indexed by TypeReference for the source
+  // type in the dex file with the "type value comparator" for deduplication. The value
+  // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`.
+  SafeMap<TypeReference, size_t, TypeReferenceValueComparator> bss_public_type_entries_;
+
+  // Map for allocating package Class entries in .bss. Indexed by TypeReference for the source
+  // type in the dex file with the "type value comparator" for deduplication. The value
+  // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`.
+  SafeMap<TypeReference, size_t, TypeReferenceValueComparator> bss_package_type_entries_;
+
   // Map for allocating String entries in .bss. Indexed by StringReference for the source
   // string in the dex file with the "string value comparator" for deduplication. The value
   // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`.
@@ -519,6 +535,8 @@
   uint32_t size_oat_dex_file_dex_layout_sections_alignment_;
   uint32_t size_oat_dex_file_method_bss_mapping_offset_;
   uint32_t size_oat_dex_file_type_bss_mapping_offset_;
+  uint32_t size_oat_dex_file_public_type_bss_mapping_offset_;
+  uint32_t size_oat_dex_file_package_type_bss_mapping_offset_;
   uint32_t size_oat_dex_file_string_bss_mapping_offset_;
   uint32_t size_oat_lookup_table_alignment_;
   uint32_t size_oat_lookup_table_;
@@ -530,6 +548,8 @@
   uint32_t size_oat_class_method_offsets_;
   uint32_t size_method_bss_mappings_;
   uint32_t size_type_bss_mappings_;
+  uint32_t size_public_type_bss_mappings_;
+  uint32_t size_package_type_bss_mappings_;
   uint32_t size_string_bss_mappings_;
 
   // The helper for processing relative patches is external so that we can patch across oat files.
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 838b5b5..c576640 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -85,12 +85,22 @@
   DCHECK(dex_file != nullptr);
   const OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
   if (oat_dex_file != nullptr) {
-    size_t bss_offset = IndexBssMappingLookup::GetBssOffset(oat_dex_file->GetTypeBssMapping(),
-                                                            type_idx.index_,
-                                                            dex_file->NumTypeIds(),
-                                                            sizeof(GcRoot<mirror::Class>));
-    if (bss_offset != IndexBssMappingLookup::npos) {
-      StoreObjectInBss(outer_method, oat_dex_file->GetOatFile(), bss_offset, resolved_type);
+    auto store = [=](const IndexBssMapping* mapping) REQUIRES_SHARED(Locks::mutator_lock_) {
+      size_t bss_offset = IndexBssMappingLookup::GetBssOffset(mapping,
+                                                              type_idx.index_,
+                                                              dex_file->NumTypeIds(),
+                                                              sizeof(GcRoot<mirror::Class>));
+      if (bss_offset != IndexBssMappingLookup::npos) {
+        StoreObjectInBss(outer_method, oat_dex_file->GetOatFile(), bss_offset, resolved_type);
+      }
+    };
+    store(oat_dex_file->GetTypeBssMapping());
+    if (resolved_type->IsPublic()) {
+      store(oat_dex_file->GetPublicTypeBssMapping());
+    }
+    if (resolved_type->IsPublic() ||
+        resolved_type->GetClassLoader() == outer_method->GetClassLoader()) {
+      store(oat_dex_file->GetPackageTypeBssMapping());
     }
   }
 }
@@ -180,7 +190,9 @@
                                                         self,
                                                         /* can_run_clinit= */ false,
                                                         /* verify_access= */ true);
-  // Do not StoreTypeInBss(); access check entrypoint is never used together with .bss.
+  if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) {
+    StoreTypeInBss(caller_and_outer.outer_method, dex::TypeIndex(type_idx), result);
+  }
   return result.Ptr();
 }
 
diff --git a/runtime/oat.h b/runtime/oat.h
index 960e878..558c1e5 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
-  // Last oat version changed reason: Removal of Thread->tls32_.debug_suspend_count
-  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '5', '\0' } };
+  // Last oat version changed reason: Use .bss for ResoveTypeAndVerifyAccess.
+  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '6', '\0' } };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 7a89abe..499d90b 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -883,12 +883,18 @@
 
     const IndexBssMapping* method_bss_mapping;
     const IndexBssMapping* type_bss_mapping;
+    const IndexBssMapping* public_type_bss_mapping;
+    const IndexBssMapping* package_type_bss_mapping;
     const IndexBssMapping* string_bss_mapping;
     if (!ReadIndexBssMapping(
             this, &oat, i, dex_file_location, "method", &method_bss_mapping, error_msg) ||
         !ReadIndexBssMapping(
             this, &oat, i, dex_file_location, "type", &type_bss_mapping, error_msg) ||
         !ReadIndexBssMapping(
+            this, &oat, i, dex_file_location, "type", &public_type_bss_mapping, error_msg) ||
+        !ReadIndexBssMapping(
+            this, &oat, i, dex_file_location, "type", &package_type_bss_mapping, error_msg) ||
+        !ReadIndexBssMapping(
             this, &oat, i, dex_file_location, "string", &string_bss_mapping, error_msg)) {
       return false;
     }
@@ -903,6 +909,8 @@
         lookup_table_data,
         method_bss_mapping,
         type_bss_mapping,
+        public_type_bss_mapping,
+        package_type_bss_mapping,
         string_bss_mapping,
         class_offsets_pointer,
         dex_layout_sections);
@@ -1869,6 +1877,8 @@
                        const uint8_t* lookup_table_data,
                        const IndexBssMapping* method_bss_mapping_data,
                        const IndexBssMapping* type_bss_mapping_data,
+                       const IndexBssMapping* public_type_bss_mapping_data,
+                       const IndexBssMapping* package_type_bss_mapping_data,
                        const IndexBssMapping* string_bss_mapping_data,
                        const uint32_t* oat_class_offsets_pointer,
                        const DexLayoutSections* dex_layout_sections)
@@ -1880,6 +1890,8 @@
       lookup_table_data_(lookup_table_data),
       method_bss_mapping_(method_bss_mapping_data),
       type_bss_mapping_(type_bss_mapping_data),
+      public_type_bss_mapping_(public_type_bss_mapping_data),
+      package_type_bss_mapping_(package_type_bss_mapping_data),
       string_bss_mapping_(string_bss_mapping_data),
       oat_class_offsets_pointer_(oat_class_offsets_pointer),
       lookup_table_(),
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 039c3de..9e21b7e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -522,6 +522,14 @@
     return type_bss_mapping_;
   }
 
+  const IndexBssMapping* GetPublicTypeBssMapping() const {
+    return public_type_bss_mapping_;
+  }
+
+  const IndexBssMapping* GetPackageTypeBssMapping() const {
+    return package_type_bss_mapping_;
+  }
+
   const IndexBssMapping* GetStringBssMapping() const {
     return string_bss_mapping_;
   }
@@ -565,6 +573,8 @@
              const uint8_t* lookup_table_data,
              const IndexBssMapping* method_bss_mapping,
              const IndexBssMapping* type_bss_mapping,
+             const IndexBssMapping* public_type_bss_mapping,
+             const IndexBssMapping* package_type_bss_mapping,
              const IndexBssMapping* string_bss_mapping,
              const uint32_t* oat_class_offsets_pointer,
              const DexLayoutSections* dex_layout_sections);
@@ -588,6 +598,8 @@
   const uint8_t* const lookup_table_data_ = nullptr;
   const IndexBssMapping* const method_bss_mapping_ = nullptr;
   const IndexBssMapping* const type_bss_mapping_ = nullptr;
+  const IndexBssMapping* const public_type_bss_mapping_ = nullptr;
+  const IndexBssMapping* const package_type_bss_mapping_ = nullptr;
   const IndexBssMapping* const string_bss_mapping_ = nullptr;
   const uint32_t* const oat_class_offsets_pointer_ = nullptr;
   TypeLookupTable lookup_table_;
diff --git a/test/727-checker-unresolved-class/src/unresolved/UnresolvedPublicClass.java b/test/727-checker-unresolved-class/src/unresolved/UnresolvedPublicClass.java
index 073b03e..4864e64 100644
--- a/test/727-checker-unresolved-class/src/unresolved/UnresolvedPublicClass.java
+++ b/test/727-checker-unresolved-class/src/unresolved/UnresolvedPublicClass.java
@@ -18,6 +18,8 @@
 
 import getters.GetUnresolvedPublicClass;
 import getters.GetUnresolvedPublicClassFromDifferentDexFile;
+import resolved.PackagePrivateSubclassOfUnresolvedClass;
+import resolved.PublicSubclassOfUnresolvedClass;
 import resolved.ResolvedPackagePrivateClass;
 import resolved.ResolvedPublicSubclassOfPackagePrivateClass;
 
@@ -31,6 +33,10 @@
 
     $noinline$testResolvedPublicClass();
     $noinline$testResolvedPackagePrivateClass();
+    $noinline$testUnresolvedPublicClass();
+    $noinline$testUnresolvedPackagePrivateClass();
+    $noinline$testUnresolvedPublicClassInSamePackage();
+    $noinline$testUnresolvedPackagePrivateClassInSamePackage();
 
     $noinline$testPublicFieldInResolvedPackagePrivateClass();
     $noinline$testPublicFieldInPackagePrivateClassViaResolvedPublicSubclass();
@@ -104,6 +110,45 @@
     } catch (IllegalAccessError expected) {}
   }
 
+  /// CHECK-START: void unresolved.UnresolvedPublicClass.$noinline$testUnresolvedPublicClass() builder (after)
+  /// CHECK: LoadClass class_name:resolved.PublicSubclassOfUnresolvedClass needs_access_check:true
+
+  /// CHECK-START-{ARM,ARM64,X86,X86_64}: void unresolved.UnresolvedPublicClass.$noinline$testUnresolvedPublicClass() builder (after)
+  /// CHECK: LoadClass load_kind:BssEntryPublic class_name:resolved.PublicSubclassOfUnresolvedClass
+  static void $noinline$testUnresolvedPublicClass() {
+    Class<?> c = PublicSubclassOfUnresolvedClass.class;
+  }
+
+  /// CHECK-START: void unresolved.UnresolvedPublicClass.$noinline$testUnresolvedPackagePrivateClass() builder (after)
+  /// CHECK: LoadClass class_name:resolved.PackagePrivateSubclassOfUnresolvedClass needs_access_check:true
+
+  /// CHECK-START-{ARM,ARM64,X86,X86_64}: void unresolved.UnresolvedPublicClass.$noinline$testUnresolvedPackagePrivateClass() builder (after)
+  /// CHECK: LoadClass load_kind:BssEntryPublic class_name:resolved.PackagePrivateSubclassOfUnresolvedClass
+  static void $noinline$testUnresolvedPackagePrivateClass() {
+    try {
+      Class<?> c = PackagePrivateSubclassOfUnresolvedClass.class;
+      throw new Error("Unreachable");
+    } catch (IllegalAccessError expected) {}
+  }
+
+  /// CHECK-START: void unresolved.UnresolvedPublicClass.$noinline$testUnresolvedPublicClassInSamePackage() builder (after)
+  /// CHECK: LoadClass class_name:unresolved.UnresolvedPublicClazz needs_access_check:true
+
+  /// CHECK-START-{ARM,ARM64,X86,X86_64}: void unresolved.UnresolvedPublicClass.$noinline$testUnresolvedPublicClassInSamePackage() builder (after)
+  /// CHECK: LoadClass load_kind:BssEntryPackage class_name:unresolved.UnresolvedPublicClazz
+  static void $noinline$testUnresolvedPublicClassInSamePackage() {
+    Class<?> c = UnresolvedPublicClazz.class;
+  }
+
+  /// CHECK-START: void unresolved.UnresolvedPublicClass.$noinline$testUnresolvedPackagePrivateClassInSamePackage() builder (after)
+  /// CHECK: LoadClass class_name:unresolved.UnresolvedPackagePrivateClass needs_access_check:true
+
+  /// CHECK-START-{ARM,ARM64,X86,X86_64}: void unresolved.UnresolvedPublicClass.$noinline$testUnresolvedPackagePrivateClassInSamePackage() builder (after)
+  /// CHECK: LoadClass load_kind:BssEntryPackage class_name:unresolved.UnresolvedPackagePrivateClass
+  static void $noinline$testUnresolvedPackagePrivateClassInSamePackage() {
+    Class<?> c = UnresolvedPackagePrivateClass.class;
+  }
+
   /// CHECK-START: void unresolved.UnresolvedPublicClass.$noinline$testPublicFieldInResolvedPackagePrivateClass() builder (after)
   /// CHECK: UnresolvedStaticFieldSet