Revert^2 "x86_64: Store resolved MethodType-s in .bss."

This reverts commit d014fd019e84471665ac02f2de285541009892cd.

Reason for revert: fix codegen to do runtime call in JIT for now.

Bug: 297147201
Test: ./art/test/testrunner/testrunner.py --host --64 --optimizing -b
Test: ./art/test/testrunner/testrunner.py --jvm -b
Test: ./art/test.py --host -b
Test: ./art/test/testrunner/testrunner.py --host --64 --jit -b

Change-Id: I0f01c8391b09659bb6195955ecd8f88159141872
diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h
index 8ed7fce..1330882 100644
--- a/compiler/linker/linker_patch.h
+++ b/compiler/linker/linker_patch.h
@@ -56,6 +56,7 @@
     kPackageTypeBssEntry,
     kStringRelative,
     kStringBssEntry,
+    kMethodTypeBssEntry,
     kCallEntrypoint,
     kBakerReadBarrierBranch,
   };
@@ -176,6 +177,16 @@
     return patch;
   }
 
+  static LinkerPatch MethodTypeBssEntryPatch(size_t literal_offset,
+                                             const DexFile* target_dex_file,
+                                             uint32_t pc_insn_offset,
+                                             uint32_t target_proto_idx) {
+    LinkerPatch patch(literal_offset, Type::kMethodTypeBssEntry, target_dex_file);
+    patch.proto_idx_ = target_proto_idx;
+    patch.pc_insn_offset_ = pc_insn_offset;
+    return patch;
+  }
+
   static LinkerPatch CallEntrypointPatch(size_t literal_offset,
                                          uint32_t entrypoint_offset) {
     LinkerPatch patch(literal_offset,
@@ -253,6 +264,16 @@
     return dex::StringIndex(string_idx_);
   }
 
+  const DexFile* TargetProtoDexFile() const {
+    DCHECK(patch_type_ == Type::kMethodTypeBssEntry);
+    return target_dex_file_;
+  }
+
+  dex::ProtoIndex TargetProtoIndex() const {
+    DCHECK(patch_type_ == Type::kMethodTypeBssEntry);
+    return dex::ProtoIndex(proto_idx_);
+  }
+
   uint32_t PcInsnOffset() const {
     DCHECK(patch_type_ == Type::kIntrinsicReference ||
            patch_type_ == Type::kDataBimgRelRo ||
@@ -305,12 +326,14 @@
     uint32_t method_idx_;         // Method index for Call/Method patches.
     uint32_t type_idx_;           // Type index for Type patches.
     uint32_t string_idx_;         // String index for String patches.
+    uint32_t proto_idx_;          // Proto index for MethodType patches.
     uint32_t intrinsic_data_;     // Data for IntrinsicObjects.
     uint32_t entrypoint_offset_;  // Entrypoint offset in the Thread object.
     uint32_t baker_custom_value1_;
     static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators");
     static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators");
     static_assert(sizeof(string_idx_) == sizeof(cmp1_), "needed by relational operators");
+    static_assert(sizeof(proto_idx_) == sizeof(cmp1_), "needed by relational operators");
     static_assert(sizeof(intrinsic_data_) == sizeof(cmp1_), "needed by relational operators");
     static_assert(sizeof(baker_custom_value1_) == sizeof(cmp1_), "needed by relational operators");
   };
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 34400c9..b0e07e3 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1682,6 +1682,7 @@
          instruction->IsArrayGet() ||
          instruction->IsArraySet() ||
          instruction->IsLoadClass() ||
+         instruction->IsLoadMethodType() ||
          instruction->IsLoadString() ||
          instruction->IsInstanceOf() ||
          instruction->IsCheckCast() ||
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index d43d3dd..69fde66 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -270,6 +270,38 @@
   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64);
 };
 
+class LoadMethodTypeSlowPathX86_64: public SlowPathCode {
+ public:
+  explicit LoadMethodTypeSlowPathX86_64(HLoadMethodType* mt) : SlowPathCode(mt) {}
+
+  void EmitNativeCode(CodeGenerator* codegen) override {
+    LocationSummary* locations = instruction_->GetLocations();
+    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+
+    CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
+    __ Bind(GetEntryLabel());
+    SaveLiveRegisters(codegen, locations);
+
+    const dex::ProtoIndex proto_index = instruction_->AsLoadMethodType()->GetProtoIndex();
+    // Custom calling convention: RAX serves as both input and output.
+    __ movl(CpuRegister(RAX), Immediate(proto_index.index_));
+    x86_64_codegen->InvokeRuntime(kQuickResolveMethodType,
+                                  instruction_,
+                                  instruction_->GetDexPc(),
+                                  this);
+    CheckEntrypointTypes<kQuickResolveMethodType, void*, uint32_t>();
+    x86_64_codegen->Move(locations->Out(), Location::RegisterLocation(RAX));
+    RestoreLiveRegisters(codegen, locations);
+
+    __ jmp(GetExitLabel());
+  }
+
+  const char* GetDescription() const override { return "LoadMethodTypeSlowPathX86_64"; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoadMethodTypeSlowPathX86_64);
+};
+
 class LoadClassSlowPathX86_64 : public SlowPathCode {
  public:
   LoadClassSlowPathX86_64(HLoadClass* cls, HInstruction* at)
@@ -529,6 +561,7 @@
            instruction_->IsArrayGet() ||
            instruction_->IsArraySet() ||
            instruction_->IsLoadClass() ||
+           instruction_->IsLoadMethodType() ||
            instruction_->IsLoadString() ||
            instruction_->IsInstanceOf() ||
            instruction_->IsCheckCast() ||
@@ -1319,6 +1352,12 @@
   return &string_bss_entry_patches_.back().label;
 }
 
+Label* CodeGeneratorX86_64::NewMethodTypeBssEntryPatch(HLoadMethodType* load_method_type) {
+  method_type_bss_entry_patches_.emplace_back(
+      &load_method_type->GetDexFile(), load_method_type->GetProtoIndex().index_);
+  return &method_type_bss_entry_patches_.back().label;
+}
+
 void CodeGeneratorX86_64::RecordBootImageJniEntrypointPatch(HInvokeStaticOrDirect* invoke) {
   boot_image_jni_entrypoint_patches_.emplace_back(invoke->GetResolvedMethodReference().dex_file,
                                                   invoke->GetResolvedMethodReference().index);
@@ -1406,6 +1445,7 @@
       package_type_bss_entry_patches_.size() +
       boot_image_string_patches_.size() +
       string_bss_entry_patches_.size() +
+      method_type_bss_entry_patches_.size() +
       boot_image_jni_entrypoint_patches_.size() +
       boot_image_other_patches_.size();
   linker_patches->reserve(size);
@@ -1438,6 +1478,8 @@
       package_type_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
       string_bss_entry_patches_, linker_patches);
+  EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodTypeBssEntryPatch>(
+      method_type_bss_entry_patches_, linker_patches);
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeJniEntrypointPatch>(
       boot_image_jni_entrypoint_patches_, linker_patches);
   DCHECK_EQ(size, linker_patches->size());
@@ -1562,6 +1604,7 @@
       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)),
+      method_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_jni_entrypoint_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
       jit_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
@@ -6673,13 +6716,50 @@
 }
 
 void LocationsBuilderX86_64::VisitLoadMethodType(HLoadMethodType* load) {
-  // Custom calling convention: RAX serves as both input and output.
-  Location location = Location::RegisterLocation(RAX);
-  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+  LocationSummary* locations =
+      new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  if (load->GetLoadKind() == HLoadMethodType::LoadKind::kRuntimeCall) {
+      Location location = Location::RegisterLocation(RAX);
+      CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+  } else {
+    DCHECK_EQ(load->GetLoadKind(), HLoadMethodType::LoadKind::kBssEntry);
+    locations->SetOut(Location::RequiresRegister());
+    if (codegen_->EmitNonBakerReadBarrier()) {
+      // For non-Baker read barrier we have a temp-clobbering call.
+    } else {
+      // Rely on the pResolveMethodType to save everything.
+      locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
+    }
+  }
 }
 
 void InstructionCodeGeneratorX86_64::VisitLoadMethodType(HLoadMethodType* load) {
-  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+  LocationSummary* locations = load->GetLocations();
+  Location out_loc = locations->Out();
+  CpuRegister out = out_loc.AsRegister<CpuRegister>();
+
+  switch (load->GetLoadKind()) {
+    case HLoadMethodType::LoadKind::kBssEntry: {
+      Address address = Address::Absolute(CodeGeneratorX86_64::kPlaceholder32BitOffset,
+                                          /* no_rip= */ false);
+      Label* fixup_label = codegen_->NewMethodTypeBssEntryPatch(load);
+      // /* GcRoot<mirror::MethodType> */ out = *address  /* PC-relative */
+      GenerateGcRootFieldLoad(
+          load, out_loc, address, fixup_label, codegen_->GetCompilerReadBarrierOption());
+      // No need for memory fence, thanks to the x86-64 memory model.
+      SlowPathCode* slow_path =
+          new (codegen_->GetScopedAllocator()) LoadMethodTypeSlowPathX86_64(load);
+      codegen_->AddSlowPath(slow_path);
+      __ testl(out, out);
+      __ j(kEqual, slow_path->GetEntryLabel());
+      __ Bind(slow_path->GetExitLabel());
+      return;
+    }
+    default:
+      DCHECK_EQ(load->GetLoadKind(), HLoadMethodType::LoadKind::kRuntimeCall);
+      codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+      break;
+  }
 }
 
 void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 7da2e39..e4d3eac 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -523,6 +523,7 @@
   Label* NewTypeBssEntryPatch(HLoadClass* load_class);
   void RecordBootImageStringPatch(HLoadString* load_string);
   Label* NewStringBssEntryPatch(HLoadString* load_string);
+  Label* NewMethodTypeBssEntryPatch(HLoadMethodType* load_method_type);
   void RecordBootImageJniEntrypointPatch(HInvokeStaticOrDirect* invoke);
   Label* NewJitRootStringPatch(const DexFile& dex_file,
                                dex::StringIndex string_index,
@@ -735,6 +736,8 @@
   ArenaDeque<PatchInfo<Label>> boot_image_string_patches_;
   // PC-relative String patch info for kBssEntry.
   ArenaDeque<PatchInfo<Label>> string_bss_entry_patches_;
+  // PC-relative MethodType patch info for kBssEntry.
+  ArenaDeque<PatchInfo<Label>> method_type_bss_entry_patches_;
   // PC-relative method patch info for kBootImageLinkTimePcRelative+kCallCriticalNative.
   ArenaDeque<PatchInfo<Label>> boot_image_jni_entrypoint_patches_;
   // PC-relative patch info for IntrinsicObjects for the boot image,
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 281da6f..fe0f3fe 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -2699,6 +2699,9 @@
   const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
   HLoadMethodType* load_method_type =
       new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_index, dex_file, dex_pc);
+  if (!code_generator_->GetCompilerOptions().IsJitCompiler()) {
+    load_method_type->SetLoadKind(HLoadMethodType::LoadKind::kBssEntry);
+  }
   AppendInstruction(load_method_type);
 }
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index d84ff7b..0efe8f4 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -7313,6 +7313,16 @@
 
 class HLoadMethodType final : public HInstruction {
  public:
+  // Determines how to load the MethodType.
+  enum class LoadKind {
+    // Load from an entry in the .bss section using a PC-relative load.
+    kBssEntry,
+    // Load using a single runtime call.
+    kRuntimeCall,
+
+    kLast = kRuntimeCall,
+  };
+
   HLoadMethodType(HCurrentMethod* current_method,
                   dex::ProtoIndex proto_index,
                   const DexFile& dex_file,
@@ -7324,6 +7334,7 @@
         special_input_(HUserRecord<HInstruction*>(current_method)),
         proto_index_(proto_index),
         dex_file_(dex_file) {
+    SetPackedField<LoadKindField>(LoadKind::kRuntimeCall);
   }
 
   using HInstruction::GetInputRecords;  // Keep the const version visible.
@@ -7334,6 +7345,12 @@
 
   bool IsClonable() const override { return true; }
 
+  void SetLoadKind(LoadKind load_kind);
+
+  LoadKind GetLoadKind() const {
+    return GetPackedField<LoadKindField>();
+  }
+
   dex::ProtoIndex GetProtoIndex() const { return proto_index_; }
 
   const DexFile& GetDexFile() const { return dex_file_; }
@@ -7352,6 +7369,14 @@
   DEFAULT_COPY_CONSTRUCTOR(LoadMethodType);
 
  private:
+  static constexpr size_t kFieldLoadKind = kNumberOfGenericPackedBits;
+  static constexpr size_t kFieldLoadKindSize =
+      MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
+  static constexpr size_t kNumberOfLoadMethodTypePackedBits = kFieldLoadKind + kFieldLoadKindSize;
+  static_assert(kNumberOfLoadMethodTypePackedBits <= kMaxNumberOfPackedBits,
+      "Too many packed fields.");
+  using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>;
+
   // The special input is the HCurrentMethod for kRuntimeCall.
   HUserRecord<HInstruction*> special_input_;
 
@@ -7359,6 +7384,17 @@
   const DexFile& dex_file_;
 };
 
+std::ostream& operator<<(std::ostream& os, HLoadMethodType::LoadKind rhs);
+
+// Note: defined outside class to see operator<<(., HLoadMethodType::LoadKind).
+inline void HLoadMethodType::SetLoadKind(LoadKind load_kind) {
+  // The load kind should be determined before inserting the instruction to the graph.
+  DCHECK(GetBlock() == nullptr);
+  DCHECK(GetEnvironment() == nullptr);
+  DCHECK_EQ(GetLoadKind(), LoadKind::kRuntimeCall);
+  SetPackedField<LoadKindField>(load_kind);
+}
+
 /**
  * Performs an initialization check on its Class object input.
  */
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64.cc b/dex2oat/linker/arm64/relative_patcher_arm64.cc
index 0c34cb8..db7ae74 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64.cc
@@ -71,6 +71,7 @@
     case LinkerPatch::Type::kPackageTypeBssEntry:
     case LinkerPatch::Type::kStringRelative:
     case LinkerPatch::Type::kStringBssEntry:
+    case LinkerPatch::Type::kMethodTypeBssEntry:
       return patch.LiteralOffset() == patch.PcInsnOffset();
   }
 }
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index f85d817..1564d50 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -47,6 +47,7 @@
 #include "dex/dex_file_loader.h"
 #include "dex/dex_file_types.h"
 #include "dex/dex_file_verifier.h"
+#include "dex/proto_reference.h"
 #include "dex/standard_dex_file.h"
 #include "dex/type_lookup_table.h"
 #include "dex/verification_results.h"
@@ -296,6 +297,7 @@
   uint32_t public_type_bss_mapping_offset_;
   uint32_t package_type_bss_mapping_offset_;
   uint32_t string_bss_mapping_offset_;
+  uint32_t method_type_bss_mapping_offset_;
 
   // Offset of dex sections that will have different runtime madvise states.
   // Set in WriteDexLayoutSections.
@@ -357,6 +359,7 @@
       bss_public_type_entries_(),
       bss_package_type_entries_(),
       bss_string_entries_(),
+      bss_method_type_entries_(),
       oat_data_offset_(0u),
       oat_header_(nullptr),
       relative_patcher_(nullptr),
@@ -750,6 +753,12 @@
                           target_string.dex_file->NumStringIds(),
                           &writer_->bss_string_entry_references_);
           writer_->bss_string_entries_.Overwrite(target_string, /* placeholder */ 0u);
+        } else if (patch.GetType() == LinkerPatch::Type::kMethodTypeBssEntry) {
+          ProtoReference target_proto(patch.TargetProtoDexFile(), patch.TargetProtoIndex());
+          AddBssReference(target_proto,
+                          target_proto.dex_file->NumProtoIds(),
+                          &writer_->bss_method_type_entry_references_);
+          writer_->bss_method_type_entries_.Overwrite(target_proto, /* placeholder */ 0u);
         }
       }
     } else {
@@ -869,6 +878,7 @@
   uint32_t public_type_bss_mapping_offset = 0u;
   uint32_t package_type_bss_mapping_offset = 0u;
   uint32_t string_bss_mapping_offset = 0u;
+  uint32_t method_type_bss_mapping_offset = 0u;
 
   // Offset of the BSSInfo start from beginning of OatHeader. It is used to validate file position
   // when writing.
@@ -879,7 +889,8 @@
            sizeof(type_bss_mapping_offset) +
            sizeof(public_type_bss_mapping_offset) +
            sizeof(package_type_bss_mapping_offset) +
-           sizeof(string_bss_mapping_offset);
+           sizeof(string_bss_mapping_offset) +
+           sizeof(method_type_bss_mapping_offset);
   }
   bool Write(OatWriter* oat_writer, OutputStream* out) const;
 };
@@ -1684,6 +1695,16 @@
                                                                    target_offset);
               break;
             }
+            case LinkerPatch::Type::kMethodTypeBssEntry: {
+              ProtoReference ref(patch.TargetProtoDexFile(), patch.TargetProtoIndex());
+              uint32_t target_offset =
+                  writer_->bss_start_ + writer_->bss_method_type_entries_.Get(ref);
+              writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+                                                                   patch,
+                                                                   offset_ + literal_offset,
+                                                                   target_offset);
+              break;
+            }
             case LinkerPatch::Type::kTypeRelative: {
               uint32_t target_offset = GetTargetObjectOffset(GetTargetType(patch));
               writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
@@ -2038,7 +2059,8 @@
       bss_type_entry_references_.empty() &&
       bss_public_type_entry_references_.empty() &&
       bss_package_type_entry_references_.empty() &&
-      bss_string_entry_references_.empty()) {
+      bss_string_entry_references_.empty() &&
+      bss_method_type_entry_references_.empty()) {
     return offset;
   }
   // If there are any classes, the class offsets allocation aligns the offset
@@ -2051,6 +2073,7 @@
   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;
+  size_t number_of_method_type_dex_files = 0u;
   for (size_t i = 0, size = dex_files_->size(); i != size; ++i) {
     const DexFile* dex_file = (*dex_files_)[i];
     offset = InitIndexBssMappingsHelper(offset,
@@ -2060,11 +2083,13 @@
                                         number_of_public_type_dex_files,
                                         number_of_package_type_dex_files,
                                         number_of_string_dex_files,
+                                        number_of_method_type_dex_files,
                                         oat_dex_files_[i].method_bss_mapping_offset_,
                                         oat_dex_files_[i].type_bss_mapping_offset_,
                                         oat_dex_files_[i].public_type_bss_mapping_offset_,
                                         oat_dex_files_[i].package_type_bss_mapping_offset_,
-                                        oat_dex_files_[i].string_bss_mapping_offset_);
+                                        oat_dex_files_[i].string_bss_mapping_offset_,
+                                        oat_dex_files_[i].method_type_bss_mapping_offset_);
   }
 
   if (!(compiler_options_.IsBootImage() || compiler_options_.IsBootImageExtension())) {
@@ -2086,11 +2111,13 @@
                                           number_of_public_type_dex_files,
                                           number_of_package_type_dex_files,
                                           number_of_string_dex_files,
+                                          number_of_method_type_dex_files,
                                           bcp_bss_info_[i].method_bss_mapping_offset,
                                           bcp_bss_info_[i].type_bss_mapping_offset,
                                           bcp_bss_info_[i].public_type_bss_mapping_offset,
                                           bcp_bss_info_[i].package_type_bss_mapping_offset,
-                                          bcp_bss_info_[i].string_bss_mapping_offset);
+                                          bcp_bss_info_[i].string_bss_mapping_offset,
+                                          bcp_bss_info_[i].method_type_bss_mapping_offset);
     }
   }
 
@@ -2101,6 +2128,7 @@
   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());
+  CHECK_EQ(number_of_method_type_dex_files, bss_method_type_entry_references_.size());
 
   return offset;
 }
@@ -2112,11 +2140,13 @@
                                              /*inout*/ size_t& number_of_public_type_dex_files,
                                              /*inout*/ size_t& number_of_package_type_dex_files,
                                              /*inout*/ size_t& number_of_string_dex_files,
+                                             /*inout*/ size_t& number_of_method_type_dex_files,
                                              /*inout*/ uint32_t& method_bss_mapping_offset,
                                              /*inout*/ uint32_t& type_bss_mapping_offset,
                                              /*inout*/ uint32_t& public_type_bss_mapping_offset,
                                              /*inout*/ uint32_t& package_type_bss_mapping_offset,
-                                             /*inout*/ uint32_t& string_bss_mapping_offset) {
+                                             /*inout*/ uint32_t& string_bss_mapping_offset,
+                                             /*inout*/ uint32_t& method_type_bss_mapping_offset) {
   const PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet());
   auto method_it = bss_method_entry_references_.find(dex_file);
   if (method_it != bss_method_entry_references_.end()) {
@@ -2168,6 +2198,21 @@
           return bss_string_entries_.Get({dex_file, dex::StringIndex(index)});
         });
   }
+
+  auto method_type_it = bss_method_type_entry_references_.find(dex_file);
+  if (method_type_it != bss_method_type_entry_references_.end()) {
+    const BitVector& proto_indexes = method_type_it->second;
+    ++number_of_method_type_dex_files;
+    method_type_bss_mapping_offset = offset;
+    offset += CalculateIndexBssMappingSize(
+        dex_file->NumProtoIds(),
+        sizeof(GcRoot<mirror::MethodType>),
+        proto_indexes,
+        [=](uint32_t index) {
+          return bss_method_type_entries_.Get({dex_file, dex::ProtoIndex(index)});
+        });
+  }
+
   return offset;
 }
 
@@ -2339,7 +2384,8 @@
       bss_type_entries_.empty() &&
       bss_public_type_entries_.empty() &&
       bss_package_type_entries_.empty() &&
-      bss_string_entries_.empty()) {
+      bss_string_entries_.empty() &&
+      bss_method_type_entries_.empty()) {
     // Nothing to put to the .bss section.
     return;
   }
@@ -2380,6 +2426,12 @@
     entry.second = bss_size_;
     bss_size_ += sizeof(GcRoot<mirror::String>);
   }
+  // Prepare offsets for .bss MethodType entries.
+  for (auto& entry : bss_method_type_entries_) {
+    DCHECK_EQ(entry.second, 0u);
+    entry.second = bss_size_;
+    bss_size_ += sizeof(GcRoot<mirror::MethodType>);
+  }
 }
 
 bool OatWriter::WriteRodata(OutputStream* out) {
@@ -2599,12 +2651,14 @@
     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_dex_file_method_type_bss_mapping_offset_);
     DO_STAT(size_bcp_bss_info_size_);
     DO_STAT(size_bcp_bss_info_method_bss_mapping_offset_);
     DO_STAT(size_bcp_bss_info_type_bss_mapping_offset_);
     DO_STAT(size_bcp_bss_info_public_type_bss_mapping_offset_);
     DO_STAT(size_bcp_bss_info_package_type_bss_mapping_offset_);
     DO_STAT(size_bcp_bss_info_string_bss_mapping_offset_);
+    DO_STAT(size_bcp_bss_info_method_type_bss_mapping_offset_);
     DO_STAT(size_oat_class_offsets_alignment_);
     DO_STAT(size_oat_class_offsets_);
     DO_STAT(size_oat_class_type_);
@@ -2617,6 +2671,7 @@
     DO_STAT(size_public_type_bss_mappings_);
     DO_STAT(size_package_type_bss_mappings_);
     DO_STAT(size_string_bss_mappings_);
+    DO_STAT(size_method_type_bss_mappings_);
     #undef DO_STAT
 
     VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)";
@@ -2802,7 +2857,8 @@
                                               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) {
+                                              uint32_t string_bss_mapping_offset,
+                                              uint32_t method_type_bss_mapping_offset) {
   const PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet());
   auto method_it = bss_method_entry_references_.find(dex_file);
   if (method_it != bss_method_entry_references_.end()) {
@@ -2896,6 +2952,29 @@
     DCHECK_EQ(0u, string_bss_mapping_offset);
   }
 
+  auto method_type_it = bss_method_type_entry_references_.find(dex_file);
+  if (method_type_it != bss_method_type_entry_references_.end()) {
+    const BitVector& method_type_indexes = method_type_it->second;
+    DCHECK_EQ(relative_offset, method_type_bss_mapping_offset);
+    DCHECK_OFFSET();
+    size_t method_type_mappings_size =
+        WriteIndexBssMapping(out,
+                             dex_file->NumProtoIds(),
+                             sizeof(GcRoot<mirror::MethodType>),
+                             method_type_indexes,
+                             [=](uint32_t index) {
+                               return bss_method_type_entries_
+                                   .Get({dex_file, dex::ProtoIndex(index)});
+                             });
+    if (method_type_mappings_size == 0u) {
+      return 0u;
+    }
+    size_method_type_bss_mappings_ += method_type_mappings_size;
+    relative_offset += method_type_mappings_size;
+  } else {
+    DCHECK_EQ(0u, method_type_bss_mapping_offset);
+  }
+
   return relative_offset;
 }
 
@@ -2907,7 +2986,8 @@
       bss_type_entry_references_.empty() &&
       bss_public_type_entry_references_.empty() &&
       bss_package_type_entry_references_.empty() &&
-      bss_string_entry_references_.empty()) {
+      bss_string_entry_references_.empty() &&
+      bss_method_type_entry_references_.empty()) {
     return relative_offset;
   }
   // If there are any classes, the class offsets allocation aligns the offset
@@ -2926,7 +3006,8 @@
                                                   oat_dex_file->type_bss_mapping_offset_,
                                                   oat_dex_file->public_type_bss_mapping_offset_,
                                                   oat_dex_file->package_type_bss_mapping_offset_,
-                                                  oat_dex_file->string_bss_mapping_offset_);
+                                                  oat_dex_file->string_bss_mapping_offset_,
+                                                  oat_dex_file->method_type_bss_mapping_offset_);
     if (relative_offset == 0u) {
       return 0u;
     }
@@ -2947,7 +3028,8 @@
                                       bcp_bss_info_[i].type_bss_mapping_offset,
                                       bcp_bss_info_[i].public_type_bss_mapping_offset,
                                       bcp_bss_info_[i].package_type_bss_mapping_offset,
-                                      bcp_bss_info_[i].string_bss_mapping_offset);
+                                      bcp_bss_info_[i].string_bss_mapping_offset,
+                                      bcp_bss_info_[i].method_type_bss_mapping_offset);
       if (relative_offset == 0u) {
         return 0u;
       }
@@ -3782,6 +3864,7 @@
       public_type_bss_mapping_offset_(0u),
       package_type_bss_mapping_offset_(0u),
       string_bss_mapping_offset_(0u),
+      method_type_bss_mapping_offset_(0u),
       dex_sections_layout_offset_(0u),
       class_offsets_() {}
 
@@ -3791,7 +3874,8 @@
          sizeof(class_offsets_offset_) + 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_);
+         sizeof(string_bss_mapping_offset_) + sizeof(method_type_bss_mapping_offset_) +
+         sizeof(dex_sections_layout_offset_);
 }
 
 bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) const {
@@ -3885,6 +3969,13 @@
   }
   oat_writer->size_oat_dex_file_string_bss_mapping_offset_ += sizeof(string_bss_mapping_offset_);
 
+  if (!out->WriteFully(&method_type_bss_mapping_offset_, sizeof(method_type_bss_mapping_offset_))) {
+    PLOG(ERROR) << "Failed to write MethodType bss mapping offset to " << out->GetLocation();
+    return false;
+  }
+  oat_writer->size_oat_dex_file_method_type_bss_mapping_offset_ +=
+      sizeof(method_type_bss_mapping_offset_);
+
   return true;
 }
 
@@ -3924,6 +4015,13 @@
   }
   oat_writer->size_bcp_bss_info_string_bss_mapping_offset_ += sizeof(string_bss_mapping_offset);
 
+  if (!out->WriteFully(&method_type_bss_mapping_offset, sizeof(method_type_bss_mapping_offset))) {
+    PLOG(ERROR) << "Failed to write method type bss mapping offset to " << out->GetLocation();
+    return false;
+  }
+  oat_writer->size_bcp_bss_info_method_type_bss_mapping_offset_ +=
+      sizeof(method_type_bss_mapping_offset);
+
   return true;
 }
 
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index 5b66950..9d83bcb 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -32,6 +32,7 @@
 #include "dex/compact_dex_level.h"
 #include "dex/method_reference.h"
 #include "dex/string_reference.h"
+#include "dex/proto_reference.h"
 #include "dex/type_reference.h"
 #include "linker/relative_patcher.h"  // For RelativePatcherTargetProvider.
 #include "mirror/class.h"
@@ -321,7 +322,8 @@
                                      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);
+                                     uint32_t string_bss_mapping_offset,
+                                     uint32_t method_type_bss_mapping_offset);
   size_t InitIndexBssMappingsHelper(size_t offset,
                                     const DexFile* dex_file,
                                     /*inout*/ size_t& number_of_method_dex_files,
@@ -329,11 +331,13 @@
                                     /*inout*/ size_t& number_of_public_type_dex_files,
                                     /*inout*/ size_t& number_of_package_type_dex_files,
                                     /*inout*/ size_t& number_of_string_dex_files,
+                                    /*inout*/ size_t& number_of_method_type_dex_files,
                                     /*inout*/ uint32_t& method_bss_mapping_offset,
                                     /*inout*/ uint32_t& type_bss_mapping_offset,
                                     /*inout*/ uint32_t& public_type_bss_mapping_offset,
                                     /*inout*/ uint32_t& package_type_bss_mapping_offset,
-                                    /*inout*/ uint32_t& string_bss_mapping_offset);
+                                    /*inout*/ uint32_t& string_bss_mapping_offset,
+                                    /*inout*/ uint32_t& method_type_bss_mapping_offset);
 
   bool RecordOatDataOffset(OutputStream* out);
   void InitializeTypeLookupTables(
@@ -449,6 +453,9 @@
   // Map for recording references to GcRoot<mirror::String> entries in .bss.
   SafeMap<const DexFile*, BitVector> bss_string_entry_references_;
 
+  // Map for recording references to GcRoot<mirror::MethodType> entries in .bss.
+  SafeMap<const DexFile*, BitVector> bss_method_type_entry_references_;
+
   // Map for allocating ArtMethod entries in .bss. Indexed by MethodReference for the target
   // method in the dex file with the "method reference value comparator" for deduplication.
   // The value is the target offset for patching, starting at `bss_start_ + bss_methods_offset_`.
@@ -474,6 +481,11 @@
   // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`.
   SafeMap<StringReference, size_t, StringReferenceValueComparator> bss_string_entries_;
 
+  // Map for allocating MethodType entries in .bss. Indexed by ProtoReference for the source
+  // proto in the dex file with the "proto value comparator" for deduplication. The value
+  // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`.
+  SafeMap<ProtoReference, size_t, ProtoReferenceValueComparator> bss_method_type_entries_;
+
   // Offset of the oat data from the start of the mmapped region of the elf file.
   size_t oat_data_offset_;
 
@@ -540,12 +552,14 @@
   uint32_t size_oat_dex_file_public_type_bss_mapping_offset_ = 0;
   uint32_t size_oat_dex_file_package_type_bss_mapping_offset_ = 0;
   uint32_t size_oat_dex_file_string_bss_mapping_offset_ = 0;
+  uint32_t size_oat_dex_file_method_type_bss_mapping_offset_ = 0;
   uint32_t size_bcp_bss_info_size_ = 0;
   uint32_t size_bcp_bss_info_method_bss_mapping_offset_ = 0;
   uint32_t size_bcp_bss_info_type_bss_mapping_offset_ = 0;
   uint32_t size_bcp_bss_info_public_type_bss_mapping_offset_ = 0;
   uint32_t size_bcp_bss_info_package_type_bss_mapping_offset_ = 0;
   uint32_t size_bcp_bss_info_string_bss_mapping_offset_ = 0;
+  uint32_t size_bcp_bss_info_method_type_bss_mapping_offset_ = 0;
   uint32_t size_oat_class_offsets_alignment_ = 0;
   uint32_t size_oat_class_offsets_ = 0;
   uint32_t size_oat_class_type_ = 0;
@@ -558,6 +572,7 @@
   uint32_t size_public_type_bss_mappings_ = 0;
   uint32_t size_package_type_bss_mappings_ = 0;
   uint32_t size_string_bss_mappings_ = 0;
+  uint32_t size_method_type_bss_mappings_ = 0;
 
   // The helper for processing relative patches is external so that we can patch across oat files.
   MultiOatRelativePatcher* relative_patcher_;
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index ce70a20..ad528c1 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -293,6 +293,7 @@
         "dex/dex_instruction_test.cc",
         "dex/fuzzer_corpus_test.cc",
         "dex/primitive_test.cc",
+        "dex/proto_reference_test.cc",
         "dex/string_reference_test.cc",
         "dex/test_dex_file_builder_test.cc",
         "dex/type_lookup_table_test.cc",
diff --git a/libdexfile/dex/method_reference.h b/libdexfile/dex/method_reference.h
index f66ac30..9d4eabe 100644
--- a/libdexfile/dex/method_reference.h
+++ b/libdexfile/dex/method_reference.h
@@ -21,6 +21,7 @@
 #include <string>
 #include "dex/dex_file.h"
 #include "dex/dex_file_reference.h"
+#include "dex/proto_reference.h"
 
 namespace art {
 
@@ -34,6 +35,9 @@
   const dex::MethodId& GetMethodId() const {
     return dex_file->GetMethodId(index);
   }
+  const art::ProtoReference GetProtoReference() const {
+    return ProtoReference(dex_file, GetMethodId().proto_idx_);
+  }
 };
 
 // Compare the actual referenced method signatures. Used for method reference deduplication.
@@ -62,27 +66,8 @@
     if (name_diff != 0) {
       return name_diff < 0;
     }
-    // And then compare proto ids, starting with return type comparison.
-    const dex::ProtoId& prid1 = mr1.dex_file->GetProtoId(mid1.proto_idx_);
-    const dex::ProtoId& prid2 = mr2.dex_file->GetProtoId(mid2.proto_idx_);
-    int return_type_diff = strcmp(mr1.dex_file->StringByTypeIdx(prid1.return_type_idx_),
-                                  mr2.dex_file->StringByTypeIdx(prid2.return_type_idx_));
-    if (return_type_diff != 0) {
-      return return_type_diff < 0;
-    }
-    // And finishing with lexicographical parameter comparison.
-    const dex::TypeList* params1 = mr1.dex_file->GetProtoParameters(prid1);
-    size_t param1_size = (params1 != nullptr) ? params1->Size() : 0u;
-    const dex::TypeList* params2 = mr2.dex_file->GetProtoParameters(prid2);
-    size_t param2_size = (params2 != nullptr) ? params2->Size() : 0u;
-    for (size_t i = 0, num = std::min(param1_size, param2_size); i != num; ++i) {
-      int param_diff = strcmp(mr1.dex_file->StringByTypeIdx(params1->GetTypeItem(i).type_idx_),
-                              mr2.dex_file->StringByTypeIdx(params2->GetTypeItem(i).type_idx_));
-      if (param_diff != 0) {
-        return param_diff < 0;
-      }
-    }
-    return param1_size < param2_size;
+    // Then compare protos.
+    return ProtoReferenceValueComparator()(mr1.GetProtoReference(), mr2.GetProtoReference());
   }
 };
 
diff --git a/libdexfile/dex/proto_reference.h b/libdexfile/dex/proto_reference.h
new file mode 100644
index 0000000..dc9a447
--- /dev/null
+++ b/libdexfile/dex/proto_reference.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_PROTO_REFERENCE_H_
+#define ART_LIBDEXFILE_DEX_PROTO_REFERENCE_H_
+
+#include <stdint.h>
+
+#include <android-base/logging.h>
+#include <string_view>
+
+#include "dex/dex_file-inl.h"
+#include "dex/dex_file_reference.h"
+#include "dex/dex_file_types.h"
+
+namespace art {
+
+// A proto is located by its DexFile and the proto_ids_ table index into that DexFile.
+class ProtoReference : public DexFileReference {
+ public:
+  ProtoReference(const DexFile* file, dex::ProtoIndex index)
+     : DexFileReference(file, index.index_) {}
+
+  dex::ProtoIndex ProtoIndex() const {
+    return dex::ProtoIndex(index);
+  }
+
+  const dex::ProtoId& ProtoId() const {
+    return dex_file->GetProtoId(ProtoIndex());
+  }
+
+  std::string_view ReturnType() const {
+    return dex_file->GetTypeDescriptorView(dex_file->GetTypeId(ProtoId().return_type_idx_));
+  }
+};
+
+struct ProtoReferenceValueComparator {
+  bool operator()(const ProtoReference& lhs, const ProtoReference& rhs) const {
+    if (lhs.dex_file == rhs.dex_file) {
+      DCHECK_EQ(lhs.index < rhs.index, SlowCompare(lhs, rhs));
+
+      return lhs.index < rhs.index;
+    } else {
+      return SlowCompare(lhs, rhs);
+    }
+  }
+
+  bool SlowCompare(const ProtoReference& lhs, const ProtoReference& rhs) const {
+    // Compare return type first.
+    const dex::ProtoId& prid1 = lhs.ProtoId();
+    const dex::ProtoId& prid2 = rhs.ProtoId();
+    int return_type_diff = lhs.ReturnType().compare(rhs.ReturnType());
+    if (return_type_diff != 0) {
+      return return_type_diff < 0;
+    }
+    // And then compare parameters lexicographically.
+    const dex::TypeList* params1 = lhs.dex_file->GetProtoParameters(prid1);
+    size_t param1_size = (params1 != nullptr) ? params1->Size() : 0u;
+    const dex::TypeList* params2 = rhs.dex_file->GetProtoParameters(prid2);
+    size_t param2_size = (params2 != nullptr) ? params2->Size() : 0u;
+    for (size_t i = 0, num = std::min(param1_size, param2_size); i != num; ++i) {
+      std::string_view l_param = lhs.dex_file->GetTypeDescriptorView(
+          lhs.dex_file->GetTypeId(params1->GetTypeItem(i).type_idx_));
+      std::string_view r_param = rhs.dex_file->GetTypeDescriptorView(
+          rhs.dex_file->GetTypeId(params2->GetTypeItem(i).type_idx_));
+
+      int param_diff = l_param.compare(r_param);
+      if (param_diff != 0) {
+        return param_diff < 0;
+      }
+    }
+    return param1_size < param2_size;
+  }
+};
+
+}  // namespace art
+
+#endif  // ART_LIBDEXFILE_DEX_PROTO_REFERENCE_H_
diff --git a/libdexfile/dex/proto_reference_test.cc b/libdexfile/dex/proto_reference_test.cc
new file mode 100644
index 0000000..f24106d
--- /dev/null
+++ b/libdexfile/dex/proto_reference_test.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex/proto_reference.h"
+#include <vector>
+
+#include "dex/dex_file_types.h"
+#include "dex/test_dex_file_builder.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(ProtoReference, WithinOneDexFile) {
+    TestDexFileBuilder builder;
+    builder.AddMethod("LClass", "()I", "sideEffect2");
+    builder.AddMethod("LClass", "(I)Ljava/lang/String;", "toString");
+    builder.AddMethod("LClass", "(II)Ljava/lang/String;", "toString");
+    builder.AddMethod("LClass", "(IJ)Ljava/lang/String;", "toString");
+    builder.AddMethod("LClass", "(JJ)Ljava/lang/String;", "toString");
+    builder.AddMethod("LClass", "()V", "sideEffect1");
+
+    std::unique_ptr<const DexFile> dex_file = builder.Build("fake location");
+    const size_t num_protos = 6u;
+    EXPECT_EQ(num_protos, dex_file->NumProtoIds());
+
+    std::vector<ProtoReference> protos;
+
+    for (size_t i = 0; i < num_protos; ++i) {
+        protos.emplace_back(ProtoReference(dex_file.get(), dex::ProtoIndex(i)));
+    }
+
+    ProtoReferenceValueComparator cmp;
+    for (size_t i = 0; i < num_protos; ++i) {
+        for (size_t j = 0; j < num_protos; ++j) {
+            EXPECT_EQ(cmp(protos[i], protos[j]), i < j)
+                << "Inconsistent at i=" << i << " and j=" << j;
+        }
+    }
+}
+
+TEST(ProtoReference, AcrossDifferentDexFiles) {
+    TestDexFileBuilder builder1;
+    builder1.AddMethod("LClass", "()I", "sideEffect2");
+    builder1.AddMethod("LClass", "(I)Ljava/lang/String;", "toString");
+    builder1.AddMethod("LClass", "(II)Ljava/lang/String;", "toString");
+    builder1.AddMethod("LClass", "(IJ)Ljava/lang/String;", "toString");
+    builder1.AddMethod("LClass", "(IJZ)Ljava/lang/String;", "toString");
+    builder1.AddMethod("LClass", "()V", "sideEffect1");
+
+    std::unique_ptr<const DexFile> dex_file1 = builder1.Build("fake location");
+    EXPECT_EQ(6u, dex_file1->NumProtoIds());
+
+    TestDexFileBuilder builder2;
+    builder2.AddMethod("LClass2", "(IJ)Ljava/lang/String;", "toString");
+    builder2.AddMethod("LClass2", "()V", "sideEffect1");
+    builder2.AddMethod("LClass2", "(I)V", "sideEffect2");
+
+    std::unique_ptr<const DexFile> dex_file2 = builder2.Build("fake location 2");
+    EXPECT_EQ(3u, dex_file2->NumProtoIds());
+
+    ProtoReference V_dex1 = ProtoReference(dex_file1.get(), dex::ProtoIndex(5));
+    ProtoReference V_dex2 = ProtoReference(dex_file2.get(), dex::ProtoIndex(1));
+
+    ProtoReferenceValueComparator cmp;
+
+    EXPECT_FALSE(cmp(V_dex1, V_dex2));
+    EXPECT_FALSE(cmp(V_dex2, V_dex1));
+
+    ProtoReference IString_dex1 = ProtoReference(dex_file1.get(), dex::ProtoIndex(1));
+    ProtoReference IIString_dex1 = ProtoReference(dex_file1.get(), dex::ProtoIndex(2));
+    ProtoReference IJString_dex1 = ProtoReference(dex_file1.get(), dex::ProtoIndex(3));
+    ProtoReference IJZString_dex1 = ProtoReference(dex_file1.get(), dex::ProtoIndex(4));
+
+    ProtoReference IJString_dex2 = ProtoReference(dex_file2.get(), dex::ProtoIndex(0));
+
+    EXPECT_TRUE(cmp(IString_dex1, V_dex2));
+
+    EXPECT_TRUE(cmp(IString_dex1, IJString_dex2));
+    EXPECT_TRUE(cmp(IIString_dex1, IJString_dex2));
+    EXPECT_FALSE(cmp(IJString_dex1, IJString_dex2));
+    EXPECT_FALSE(cmp(IJString_dex2, IJString_dex1));
+    EXPECT_FALSE(cmp(IJZString_dex1, IJString_dex2));
+
+    EXPECT_TRUE(cmp(IJString_dex2, IJZString_dex1));
+}
+
+}  // namespace art
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 76bee21..9973d1a 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -162,13 +162,51 @@
   // Perform the update if we found a mapping.
   if (mapping != nullptr) {
     size_t bss_offset = IndexBssMappingLookup::GetBssOffset(
-        mapping, string_idx.index_, dex_file->NumStringIds(), sizeof(GcRoot<mirror::Class>));
+        mapping, string_idx.index_, dex_file->NumStringIds(), sizeof(GcRoot<mirror::String>));
     if (bss_offset != IndexBssMappingLookup::npos) {
       StoreObjectInBss(outer_method, outer_oat_file, bss_offset, resolved_string);
     }
   }
 }
 
+static inline void StoreMethodTypeInBss(ArtMethod* caller,
+                                        dex::ProtoIndex proto_idx,
+                                        ObjPtr<mirror::MethodType> resolved_method_type,
+                                        ArtMethod* outer_method)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  const DexFile* dex_file = caller->GetDexFile();
+  DCHECK_NE(dex_file, nullptr);
+
+  if (outer_method->GetDexFile()->GetOatDexFile() == nullptr ||
+      outer_method->GetDexFile()->GetOatDexFile()->GetOatFile() == nullptr) {
+    // No OatFile to update.
+    return;
+  }
+  const OatFile* outer_oat_file = outer_method->GetDexFile()->GetOatDexFile()->GetOatFile();
+
+  const OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
+  const IndexBssMapping* mapping = nullptr;
+  if (oat_dex_file != nullptr && oat_dex_file->GetOatFile() == outer_oat_file) {
+    // DexFiles compiled together to an oat file case.
+    mapping = oat_dex_file->GetMethodTypeBssMapping();
+  } else {
+    // Try to find the DexFile in the BCP of the outer_method.
+    const OatFile::BssMappingInfo* mapping_info = outer_oat_file->FindBcpMappingInfo(dex_file);
+    if (mapping_info != nullptr) {
+      mapping = mapping_info->method_type_bss_mapping;
+    }
+  }
+
+  // Perform the update if we found a mapping.
+  if (mapping != nullptr) {
+    size_t bss_offset = IndexBssMappingLookup::GetBssOffset(
+        mapping, proto_idx.index_, dex_file->NumProtoIds(), sizeof(GcRoot<mirror::MethodType>));
+    if (bss_offset != IndexBssMappingLookup::npos) {
+      StoreObjectInBss(outer_method, outer_oat_file, bss_offset, resolved_method_type);
+    }
+  }
+}
+
 extern "C" mirror::Class* artInitializeStaticStorageFromCode(mirror::Class* klass, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   // Called to ensure static storage base is initialized for direct static field reads and writes.
@@ -243,6 +281,10 @@
                                                                   CalleeSaveType::kSaveEverything);
   ArtMethod* caller = caller_and_outer.caller;
   ObjPtr<mirror::MethodType> result = ResolveMethodTypeFromCode(caller, dex::ProtoIndex(proto_idx));
+  ArtMethod* outer_method = caller_and_outer.outer_method;
+  if (LIKELY(result != nullptr)) {
+    StoreMethodTypeInBss(caller, dex::ProtoIndex(proto_idx), result, outer_method);
+  }
   return result.Ptr();
 }
 
diff --git a/runtime/oat.h b/runtime/oat.h
index 48b7c5f..7ea97fd 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -44,8 +44,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
-  // Last oat version changed reason: Change suspend barrier data structure.
-  static constexpr std::array<uint8_t, 4> kOatVersion{{'2', '4', '0', '\0'}};
+  // Last oat version changed reason: store resolved MethodType-s in .bss.
+  static constexpr std::array<uint8_t, 4> kOatVersion{{'2', '4', '1', '\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 347ad7f..ce2d4fe 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1016,6 +1016,7 @@
     const IndexBssMapping* public_type_bss_mapping;
     const IndexBssMapping* package_type_bss_mapping;
     const IndexBssMapping* string_bss_mapping;
+    const IndexBssMapping* method_type_bss_mapping = nullptr;
     auto read_index_bss_mapping = [&](const char* tag, /*out*/const IndexBssMapping** mapping) {
       return ReadIndexBssMapping(this, &oat, i, dex_file_location, tag, mapping, error_msg);
     };
@@ -1023,7 +1024,8 @@
         !read_index_bss_mapping("type", &type_bss_mapping) ||
         !read_index_bss_mapping("public type", &public_type_bss_mapping) ||
         !read_index_bss_mapping("package type", &package_type_bss_mapping) ||
-        !read_index_bss_mapping("string", &string_bss_mapping)) {
+        !read_index_bss_mapping("string", &string_bss_mapping) ||
+        !read_index_bss_mapping("method type", &method_type_bss_mapping)) {
       return false;
     }
 
@@ -1043,6 +1045,7 @@
                        public_type_bss_mapping,
                        package_type_bss_mapping,
                        string_bss_mapping,
+                       method_type_bss_mapping,
                        class_offsets_pointer,
                        dex_layout_sections);
     oat_dex_files_storage_.push_back(oat_dex_file);
@@ -2192,6 +2195,7 @@
                        const IndexBssMapping* public_type_bss_mapping_data,
                        const IndexBssMapping* package_type_bss_mapping_data,
                        const IndexBssMapping* string_bss_mapping_data,
+                       const IndexBssMapping* method_type_bss_mapping_data,
                        const uint32_t* oat_class_offsets_pointer,
                        const DexLayoutSections* dex_layout_sections)
     : oat_file_(oat_file),
@@ -2208,6 +2212,7 @@
       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),
+      method_type_bss_mapping_(method_type_bss_mapping_data),
       oat_class_offsets_pointer_(oat_class_offsets_pointer),
       lookup_table_(),
       dex_layout_sections_(dex_layout_sections) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 2e1bd2e..2af1921 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -392,6 +392,7 @@
     const IndexBssMapping* public_type_bss_mapping = nullptr;
     const IndexBssMapping* package_type_bss_mapping = nullptr;
     const IndexBssMapping* string_bss_mapping = nullptr;
+    const IndexBssMapping* method_type_bss_mapping = nullptr;
   };
 
   ArrayRef<const BssMappingInfo> GetBcpBssInfo() const {
@@ -570,6 +571,10 @@
     return string_bss_mapping_;
   }
 
+  const IndexBssMapping* GetMethodTypeBssMapping() const {
+    return method_type_bss_mapping_;
+  }
+
   const uint8_t* GetDexFilePointer() const {
     return dex_file_pointer_;
   }
@@ -609,6 +614,7 @@
              const IndexBssMapping* public_type_bss_mapping,
              const IndexBssMapping* package_type_bss_mapping,
              const IndexBssMapping* string_bss_mapping,
+             const IndexBssMapping* method_type_bss_mapping,
              const uint32_t* oat_class_offsets_pointer,
              const DexLayoutSections* dex_layout_sections);
 
@@ -643,6 +649,7 @@
   const IndexBssMapping* const public_type_bss_mapping_ = nullptr;
   const IndexBssMapping* const package_type_bss_mapping_ = nullptr;
   const IndexBssMapping* const string_bss_mapping_ = nullptr;
+  const IndexBssMapping* const method_type_bss_mapping_ = nullptr;
   const uint32_t* const oat_class_offsets_pointer_ = nullptr;
   TypeLookupTable lookup_table_;
   const DexLayoutSections* const dex_layout_sections_ = nullptr;
diff --git a/test/2265-const-method-type-cached/src/Main.java b/test/2265-const-method-type-cached/src/Main.java
index 51c3374..b582e99 100644
--- a/test/2265-const-method-type-cached/src/Main.java
+++ b/test/2265-const-method-type-cached/src/Main.java
@@ -22,7 +22,11 @@
 public class Main {
     public static void main(String... args) throws Throwable {
         testEquality();
+        // Subsequent const-method-type call should take value from from .bss.
+        testEquality();
         testNonEquality();
+        testNonEquality();
+        testWithUninitializableClass();
     }
 
     private static void unreachable() {
@@ -64,6 +68,7 @@
             int[].class);
 
         assertSame(expected, actual);
+        assertSame(takesEverythingReturnsVoid(), takesEverythingReturnsVoid());
     }
 
     public static void testNonEquality() throws Throwable {
@@ -85,6 +90,32 @@
         assertNotEqual(expected, actual);
     }
 
+    @ConstantMethodType(
+        returnType = void.class,
+        parameterTypes = { UnloadableClass.class })
+    private static MethodType takesUnloadableReturnsVoid() {
+        unreachable();
+        return null;
+    }
+
+    public static volatile int x = 0;
+
+    private static class UnloadableClass {
+        static {
+            if (x == x) {
+                throw new RuntimeException("don't init me");
+            }
+        }
+    }
+
+    public static void testWithUninitializableClass() {
+        MethodType actual = takesUnloadableReturnsVoid();
+
+        MethodType expected = MethodType.methodType(void.class, UnloadableClass.class);
+
+        assertSame(expected, actual);
+    }
+
     public static void assertNotEqual(Object expected, Object actual) {
         if (Objects.equals(expected, actual)) {
             String msg = "Expected to be non equal, but got: " + expected;