Put inlined ArtMethod pointer in stack maps.

Currently done for JIT. Can be extended for AOT and inlined boot
image methods.

Also refactor the lookup of a inlined method at runtime to not
rely on the dex cache, but look at the class loader tables.

bug: 30933338
test: test-art-host, test-art-target
Change-Id: I58bd4d763b82ab8ca3023742835ac388671d1794
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index f00648f..8ee0a6b 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -927,10 +927,10 @@
   if (environment->GetParent() != nullptr) {
     // We emit the parent environment first.
     EmitEnvironment(environment->GetParent(), slow_path);
-    stack_map_stream_.BeginInlineInfoEntry(environment->GetMethodIdx(),
+    stack_map_stream_.BeginInlineInfoEntry(environment->GetMethod(),
                                            environment->GetDexPc(),
-                                           environment->GetInvokeType(),
-                                           environment->Size());
+                                           environment->Size(),
+                                           &graph_->GetDexFile());
   }
 
   // Walk over the environment, and record the location of dex registers.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 4b6a9be..494c281 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3994,7 +3994,7 @@
       break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       // Add ADRP with its PC-relative DexCache access patch.
-      const DexFile& dex_file = invoke->GetDexFile();
+      const DexFile& dex_file = invoke->GetDexFileForPcRelativeDexCache();
       uint32_t element_offset = invoke->GetDexCacheArrayOffset();
       vixl::aarch64::Label* adrp_label = NewPcRelativeDexCacheArrayPatch(dex_file, element_offset);
       EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp));
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 046d59c..318d455 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3366,7 +3366,7 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       uint32_t offset = invoke->GetDexCacheArrayOffset();
       CodeGeneratorMIPS64::PcRelativePatchInfo* info =
-          NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset);
+          NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset);
       EmitPcRelativeAddressPlaceholderHigh(info, AT);
       __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
       break;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index f13b60a..8b4f060 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4505,7 +4505,7 @@
       __ movl(temp.AsRegister<Register>(), Address(base_reg, kDummy32BitOffset));
       // Bind a new fixup label at the end of the "movl" insn.
       uint32_t offset = invoke->GetDexCacheArrayOffset();
-      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset));
+      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset));
       break;
     }
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 89f4ae0..d4bf18d 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -986,7 +986,7 @@
               Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
       // Bind a new fixup label at the end of the "movl" insn.
       uint32_t offset = invoke->GetDexCacheArrayOffset();
-      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset));
+      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset));
       break;
     }
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index 10a36c6..ae9e0fe 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -79,9 +79,10 @@
     // we need to add the dex cache arrays base as the special input.
     if (invoke->HasPcRelativeDexCache() &&
         !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARMType>(invoke, codegen_)) {
-      HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile());
+      HArmDexCacheArraysBase* base =
+          GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache());
       // Update the element offset in base.
-      DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFile());
+      DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFileForPcRelativeDexCache());
       base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex()));
       // Add the special argument base to the method.
       DCHECK(!invoke->HasCurrentMethodInput());
diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.cc b/compiler/optimizing/dex_cache_array_fixups_mips.cc
index 31fff26..798193e 100644
--- a/compiler/optimizing/dex_cache_array_fixups_mips.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_mips.cc
@@ -74,9 +74,10 @@
     if (invoke->HasPcRelativeDexCache() &&
         !IsCallFreeIntrinsic<IntrinsicLocationsBuilderMIPS>(invoke, codegen_)) {
       // Initialize base for target method dex file if needed.
-      HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile());
+      HMipsDexCacheArraysBase* base =
+          GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache());
       // Update the element offset in base.
-      DexCacheArraysLayout layout(kMipsPointerSize, &invoke->GetDexFile());
+      DexCacheArraysLayout layout(kMipsPointerSize, &invoke->GetDexFileForPcRelativeDexCache());
       base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex()));
       // Add the special argument base to the method.
       DCHECK(!invoke->HasCurrentMethodInput());
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 768b1d8..5a75a01 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -207,10 +207,8 @@
     HEnvironment* environment = new (arena_) HEnvironment(
         arena_,
         current_locals_->size(),
-        graph_->GetDexFile(),
-        graph_->GetMethodIdx(),
+        graph_->GetArtMethod(),
         instruction->GetDexPc(),
-        graph_->GetInvokeType(),
         instruction);
     environment->CopyFrom(*current_locals_);
     instruction->SetRawEnvironment(environment);
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index fc6ff7b..17d683f 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -145,7 +145,7 @@
           if (!CheckInvokeType(intrinsic, invoke)) {
             LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
                 << intrinsic << " for "
-                << invoke->GetDexFile().PrettyMethod(invoke->GetDexMethodIndex())
+                << art_method->PrettyMethod()
                 << invoke->DebugName();
           } else {
             invoke->SetIntrinsic(intrinsic,
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index d45fa11..f7c2f97 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2387,6 +2387,14 @@
   return !opt.GetDoesNotNeedEnvironment();
 }
 
+const DexFile& HInvokeStaticOrDirect::GetDexFileForPcRelativeDexCache() const {
+  ArtMethod* caller = GetEnvironment()->GetMethod();
+  ScopedObjectAccess soa(Thread::Current());
+  // `caller` is null for a top-level graph representing a method whose declaring
+  // class was not resolved.
+  return caller == nullptr ? GetBlock()->GetGraph()->GetDexFile() : *caller->GetDexFile();
+}
+
 bool HInvokeStaticOrDirect::NeedsDexCacheOfDeclaringClass() const {
   if (GetMethodLoadKind() != MethodLoadKind::kDexCacheViaMethod) {
     return false;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 7d6f616..e32c661 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1720,28 +1720,22 @@
  public:
   HEnvironment(ArenaAllocator* arena,
                size_t number_of_vregs,
-               const DexFile& dex_file,
-               uint32_t method_idx,
+               ArtMethod* method,
                uint32_t dex_pc,
-               InvokeType invoke_type,
                HInstruction* holder)
      : vregs_(number_of_vregs, arena->Adapter(kArenaAllocEnvironmentVRegs)),
        locations_(number_of_vregs, arena->Adapter(kArenaAllocEnvironmentLocations)),
        parent_(nullptr),
-       dex_file_(dex_file),
-       method_idx_(method_idx),
+       method_(method),
        dex_pc_(dex_pc),
-       invoke_type_(invoke_type),
        holder_(holder) {
   }
 
   HEnvironment(ArenaAllocator* arena, const HEnvironment& to_copy, HInstruction* holder)
       : HEnvironment(arena,
                      to_copy.Size(),
-                     to_copy.GetDexFile(),
-                     to_copy.GetMethodIdx(),
+                     to_copy.GetMethod(),
                      to_copy.GetDexPc(),
-                     to_copy.GetInvokeType(),
                      holder) {}
 
   void SetAndCopyParentChain(ArenaAllocator* allocator, HEnvironment* parent) {
@@ -1790,16 +1784,8 @@
     return dex_pc_;
   }
 
-  uint32_t GetMethodIdx() const {
-    return method_idx_;
-  }
-
-  InvokeType GetInvokeType() const {
-    return invoke_type_;
-  }
-
-  const DexFile& GetDexFile() const {
-    return dex_file_;
+  ArtMethod* GetMethod() const {
+    return method_;
   }
 
   HInstruction* GetHolder() const {
@@ -1815,10 +1801,8 @@
   ArenaVector<HUserRecord<HEnvironment*>> vregs_;
   ArenaVector<Location> locations_;
   HEnvironment* parent_;
-  const DexFile& dex_file_;
-  const uint32_t method_idx_;
+  ArtMethod* method_;
   const uint32_t dex_pc_;
-  const InvokeType invoke_type_;
 
   // The instruction that holds this environment.
   HInstruction* const holder_;
@@ -3868,7 +3852,6 @@
   Primitive::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); }
 
   uint32_t GetDexMethodIndex() const { return dex_method_index_; }
-  const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); }
 
   InvokeType GetInvokeType() const {
     return GetPackedField<InvokeTypeField>();
@@ -4166,6 +4149,8 @@
     return dispatch_info_.method_load_data;
   }
 
+  const DexFile& GetDexFileForPcRelativeDexCache() const;
+
   ClinitCheckRequirement GetClinitCheckRequirement() const {
     return GetPackedField<ClinitCheckRequirementField>();
   }
@@ -5425,10 +5410,10 @@
   HBoundsCheck(HInstruction* index,
                HInstruction* length,
                uint32_t dex_pc,
-               uint32_t string_char_at_method_index = DexFile::kDexNoIndex)
-      : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc),
-        string_char_at_method_index_(string_char_at_method_index) {
+               bool string_char_at = false)
+      : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
     DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType()));
+    SetPackedFlag<kFlagIsStringCharAt>(string_char_at);
     SetRawInputAt(0, index);
     SetRawInputAt(1, length);
   }
@@ -5442,22 +5427,14 @@
 
   bool CanThrow() const OVERRIDE { return true; }
 
-  bool IsStringCharAt() const { return GetStringCharAtMethodIndex() != DexFile::kDexNoIndex; }
-  uint32_t GetStringCharAtMethodIndex() const { return string_char_at_method_index_; }
+  bool IsStringCharAt() const { return GetPackedFlag<kFlagIsStringCharAt>(); }
 
   HInstruction* GetIndex() const { return InputAt(0); }
 
   DECLARE_INSTRUCTION(BoundsCheck);
 
  private:
-  // We treat a String as an array, creating the HBoundsCheck from String.charAt()
-  // intrinsic in the instruction simplifier. We want to include the String.charAt()
-  // in the stack trace if we actually throw the StringIndexOutOfBoundsException,
-  // so we need to create an HEnvironment which will be translated to an InlineInfo
-  // indicating the extra stack frame. Since we add this HEnvironment quite late,
-  // in the PrepareForRegisterAllocation pass, we need to remember the method index
-  // from the invoke as we don't want to look again at the dex bytecode.
-  uint32_t string_char_at_method_index_;  // DexFile::kDexNoIndex if regular array.
+  static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits;
 
   DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
 };
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 5d9a652..7686ba8 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -52,7 +52,7 @@
   exit_block->AddInstruction(new (&allocator) HExit());
 
   HEnvironment* environment = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, null_check);
+      &allocator, 1, graph->GetArtMethod(), 0, null_check);
   null_check->SetRawEnvironment(environment);
   environment->SetRawEnvAt(0, parameter);
   parameter->AddEnvUseAt(null_check->GetEnvironment(), 0);
@@ -137,7 +137,7 @@
   ASSERT_TRUE(parameter1->GetUses().HasExactlyOneElement());
 
   HEnvironment* environment = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, with_environment);
+      &allocator, 1, graph->GetArtMethod(), 0, with_environment);
   ArenaVector<HInstruction*> array(allocator.Adapter());
   array.push_back(parameter1);
 
@@ -148,13 +148,13 @@
   ASSERT_TRUE(parameter1->GetEnvUses().HasExactlyOneElement());
 
   HEnvironment* parent1 = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr);
+      &allocator, 1, graph->GetArtMethod(), 0, nullptr);
   parent1->CopyFrom(array);
 
   ASSERT_EQ(parameter1->GetEnvUses().SizeSlow(), 2u);
 
   HEnvironment* parent2 = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr);
+      &allocator, 1, graph->GetArtMethod(), 0, nullptr);
   parent2->CopyFrom(array);
   parent1->SetAndCopyParentChain(&allocator, parent2);
 
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index f9ac3a0..d6a6efe 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -16,6 +16,9 @@
 
 #include "prepare_for_register_allocation.h"
 
+#include "jni_internal.h"
+#include "well_known_classes.h"
+
 namespace art {
 
 void PrepareForRegisterAllocation::Run() {
@@ -42,16 +45,12 @@
   if (check->IsStringCharAt()) {
     // Add a fake environment for String.charAt() inline info as we want
     // the exception to appear as being thrown from there.
-    const DexFile& dex_file = check->GetEnvironment()->GetDexFile();
-    DCHECK_STREQ(dex_file.PrettyMethod(check->GetStringCharAtMethodIndex()).c_str(),
-                 "char java.lang.String.charAt(int)");
+    ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
     ArenaAllocator* arena = GetGraph()->GetArena();
     HEnvironment* environment = new (arena) HEnvironment(arena,
                                                          /* number_of_vregs */ 0u,
-                                                         dex_file,
-                                                         check->GetStringCharAtMethodIndex(),
+                                                         char_at_method,
                                                          /* dex_pc */ DexFile::kDexNoIndex,
-                                                         kVirtual,
                                                          check);
     check->InsertRawEnvironment(environment);
   }
@@ -232,8 +231,7 @@
       return false;
     }
     if (user_environment->GetDexPc() != input_environment->GetDexPc() ||
-        user_environment->GetMethodIdx() != input_environment->GetMethodIdx() ||
-        !IsSameDexFile(user_environment->GetDexFile(), input_environment->GetDexFile())) {
+        user_environment->GetMethod() != input_environment->GetMethod()) {
       return false;
     }
     user_environment = user_environment->GetParent();
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index f8a4469..213a5ac 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -499,18 +499,19 @@
   if (instr->IsInvokeStaticOrDirect() && instr->AsInvokeStaticOrDirect()->IsStringInit()) {
     // Calls to String.<init> are replaced with a StringFactory.
     if (kIsDebugBuild) {
-      HInvoke* invoke = instr->AsInvoke();
+      HInvokeStaticOrDirect* invoke = instr->AsInvokeStaticOrDirect();
       ClassLinker* cl = Runtime::Current()->GetClassLinker();
       Thread* self = Thread::Current();
       StackHandleScope<2> hs(self);
+      const DexFile& dex_file = *invoke->GetTargetMethod().dex_file;
       Handle<mirror::DexCache> dex_cache(
-          hs.NewHandle(FindDexCacheWithHint(self, invoke->GetDexFile(), hint_dex_cache_)));
+          hs.NewHandle(FindDexCacheWithHint(self, dex_file, hint_dex_cache_)));
       // Use a null loader. We should probably use the compiling method's class loader,
       // but then we would need to pass it to RTPVisitor just for this debug check. Since
       // the method is from the String class, the null loader is good enough.
       Handle<mirror::ClassLoader> loader;
       ArtMethod* method = cl->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
-          invoke->GetDexFile(), invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect);
+          dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect);
       DCHECK(method != nullptr);
       mirror::Class* declaring_class = method->GetDeclaringClass();
       DCHECK(declaring_class != nullptr);
@@ -844,10 +845,8 @@
 
   ScopedObjectAccess soa(Thread::Current());
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
-  mirror::DexCache* dex_cache =
-      FindDexCacheWithHint(soa.Self(), instr->GetDexFile(), hint_dex_cache_);
   PointerSize pointer_size = cl->GetImagePointerSize();
-  ArtMethod* method = dex_cache->GetResolvedMethod(instr->GetDexMethodIndex(), pointer_size);
+  ArtMethod* method = instr->GetResolvedMethod();
   mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size);
   SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
 }
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index fc8af64..6087e36 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -13,8 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "stack_map_stream.h"
 
+#include "art_method.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+
 namespace art {
 
 void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
@@ -98,15 +103,27 @@
   current_dex_register_++;
 }
 
-void StackMapStream::BeginInlineInfoEntry(uint32_t method_index,
+static bool EncodeArtMethodInInlineInfo(ArtMethod* method ATTRIBUTE_UNUSED) {
+  // Note: the runtime is null only for unit testing.
+  return Runtime::Current() == nullptr || !Runtime::Current()->IsAotCompiler();
+}
+
+void StackMapStream::BeginInlineInfoEntry(ArtMethod* method,
                                           uint32_t dex_pc,
-                                          InvokeType invoke_type,
-                                          uint32_t num_dex_registers) {
+                                          uint32_t num_dex_registers,
+                                          const DexFile* outer_dex_file) {
   DCHECK(!in_inline_frame_);
   in_inline_frame_ = true;
-  current_inline_info_.method_index = method_index;
+  if (EncodeArtMethodInInlineInfo(method)) {
+    current_inline_info_.method = method;
+  } else {
+    if (dex_pc != static_cast<uint32_t>(-1) && kIsDebugBuild) {
+      ScopedObjectAccess soa(Thread::Current());
+      DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile()));
+    }
+    current_inline_info_.method_index = method->GetDexMethodIndexUnchecked();
+  }
   current_inline_info_.dex_pc = dex_pc;
-  current_inline_info_.invoke_type = invoke_type;
   current_inline_info_.num_dex_registers = num_dex_registers;
   current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size();
   if (num_dex_registers != 0) {
@@ -229,25 +246,32 @@
 void StackMapStream::ComputeInlineInfoEncoding() {
   uint32_t method_index_max = 0;
   uint32_t dex_pc_max = DexFile::kDexNoIndex;
-  uint32_t invoke_type_max = 0;
+  uint32_t extra_data_max = 0;
 
   uint32_t inline_info_index = 0;
   for (const StackMapEntry& entry : stack_maps_) {
     for (size_t j = 0; j < entry.inlining_depth; ++j) {
       InlineInfoEntry inline_entry = inline_infos_[inline_info_index++];
-      method_index_max = std::max(method_index_max, inline_entry.method_index);
+      if (inline_entry.method == nullptr) {
+        method_index_max = std::max(method_index_max, inline_entry.method_index);
+        extra_data_max = std::max(extra_data_max, 1u);
+      } else {
+        method_index_max = std::max(
+            method_index_max, High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
+        extra_data_max = std::max(
+            extra_data_max, Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
+      }
       if (inline_entry.dex_pc != DexFile::kDexNoIndex &&
           (dex_pc_max == DexFile::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) {
         dex_pc_max = inline_entry.dex_pc;
       }
-      invoke_type_max = std::max(invoke_type_max, static_cast<uint32_t>(inline_entry.invoke_type));
     }
   }
   DCHECK_EQ(inline_info_index, inline_infos_.size());
 
   inline_info_encoding_.SetFromSizes(method_index_max,
                                      dex_pc_max,
-                                     invoke_type_max,
+                                     extra_data_max,
                                      dex_register_maps_size_);
 }
 
@@ -354,9 +378,20 @@
       DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size());
       for (size_t depth = 0; depth < entry.inlining_depth; ++depth) {
         InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index];
-        inline_info.SetMethodIndexAtDepth(inline_info_encoding_, depth, inline_entry.method_index);
+        if (inline_entry.method != nullptr) {
+          inline_info.SetMethodIndexAtDepth(
+              inline_info_encoding_,
+              depth,
+              High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
+          inline_info.SetExtraDataAtDepth(
+              inline_info_encoding_,
+              depth,
+              Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
+        } else {
+          inline_info.SetMethodIndexAtDepth(inline_info_encoding_, depth, inline_entry.method_index);
+          inline_info.SetExtraDataAtDepth(inline_info_encoding_, depth, 1);
+        }
         inline_info.SetDexPcAtDepth(inline_info_encoding_, depth, inline_entry.dex_pc);
-        inline_info.SetInvokeTypeAtDepth(inline_info_encoding_, depth, inline_entry.invoke_type);
         if (inline_entry.num_dex_registers == 0) {
           // No dex map available.
           inline_info.SetDexRegisterMapOffsetAtDepth(inline_info_encoding_,
@@ -544,10 +579,13 @@
         InlineInfoEntry inline_entry = inline_infos_[inline_info_index];
         DCHECK_EQ(inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, d),
                   inline_entry.dex_pc);
-        DCHECK_EQ(inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, d),
-                  inline_entry.method_index);
-        DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, d),
-                  inline_entry.invoke_type);
+        if (inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, d)) {
+          DCHECK_EQ(inline_info.GetArtMethodAtDepth(encoding.inline_info_encoding, d),
+                    inline_entry.method);
+        } else {
+          DCHECK_EQ(inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, d),
+                    inline_entry.method_index);
+        }
 
         CheckDexRegisterMap(code_info,
                             code_info.GetDexRegisterMapAtDepth(
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 53a9795..d6f42b3 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -109,8 +109,8 @@
 
   struct InlineInfoEntry {
     uint32_t dex_pc;  // DexFile::kDexNoIndex for intrinsified native methods.
+    ArtMethod* method;
     uint32_t method_index;
-    InvokeType invoke_type;
     uint32_t num_dex_registers;
     BitVector* live_dex_registers_mask;
     size_t dex_register_locations_start_index;
@@ -126,10 +126,10 @@
 
   void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value);
 
-  void BeginInlineInfoEntry(uint32_t method_index,
+  void BeginInlineInfoEntry(ArtMethod* method,
                             uint32_t dex_pc,
-                            InvokeType invoke_type,
-                            uint32_t num_dex_registers);
+                            uint32_t num_dex_registers,
+                            const DexFile* outer_dex_file = nullptr);
   void EndInlineInfoEntry();
 
   size_t GetNumberOfStackMaps() const {
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 967fd96..22810ea 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -16,6 +16,7 @@
 
 #include "stack_map.h"
 
+#include "art_method.h"
 #include "base/arena_bit_vector.h"
 #include "stack_map_stream.h"
 
@@ -128,6 +129,7 @@
   ArenaPool pool;
   ArenaAllocator arena(&pool);
   StackMapStream stream(&arena);
+  ArtMethod art_method;
 
   ArenaBitVector sp_mask1(&arena, 0, true);
   sp_mask1.SetBit(2);
@@ -137,9 +139,9 @@
   stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2);
   stream.AddDexRegisterEntry(Kind::kInStack, 0);         // Short location.
   stream.AddDexRegisterEntry(Kind::kConstant, -2);       // Large location.
-  stream.BeginInlineInfoEntry(82, 3, kDirect, number_of_dex_registers_in_inline_info);
+  stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(42, 2, kStatic, number_of_dex_registers_in_inline_info);
+  stream.BeginInlineInfoEntry(&art_method, 2, number_of_dex_registers_in_inline_info);
   stream.EndInlineInfoEntry();
   stream.EndStackMapEntry();
 
@@ -238,12 +240,10 @@
     ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map_encoding));
     InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
     ASSERT_EQ(2u, inline_info.GetDepth(encoding.inline_info_encoding));
-    ASSERT_EQ(82u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(42u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
     ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
     ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(kDirect, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(kStatic, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+    ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0));
+    ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1));
   }
 
   // Second stack map.
@@ -662,6 +662,7 @@
   ArenaPool pool;
   ArenaAllocator arena(&pool);
   StackMapStream stream(&arena);
+  ArtMethod art_method;
 
   ArenaBitVector sp_mask1(&arena, 0, true);
   sp_mask1.SetBit(2);
@@ -672,10 +673,10 @@
   stream.AddDexRegisterEntry(Kind::kInStack, 0);
   stream.AddDexRegisterEntry(Kind::kConstant, 4);
 
-  stream.BeginInlineInfoEntry(42, 2, kStatic, 1);
+  stream.BeginInlineInfoEntry(&art_method, 2, 1);
   stream.AddDexRegisterEntry(Kind::kInStack, 8);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(82, 3, kStatic, 3);
+  stream.BeginInlineInfoEntry(&art_method, 3, 3);
   stream.AddDexRegisterEntry(Kind::kInStack, 16);
   stream.AddDexRegisterEntry(Kind::kConstant, 20);
   stream.AddDexRegisterEntry(Kind::kInRegister, 15);
@@ -688,15 +689,15 @@
   stream.AddDexRegisterEntry(Kind::kInStack, 56);
   stream.AddDexRegisterEntry(Kind::kConstant, 0);
 
-  stream.BeginInlineInfoEntry(42, 2, kDirect, 1);
+  stream.BeginInlineInfoEntry(&art_method, 2, 1);
   stream.AddDexRegisterEntry(Kind::kInStack, 12);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(82, 3, kStatic, 3);
+  stream.BeginInlineInfoEntry(&art_method, 3, 3);
   stream.AddDexRegisterEntry(Kind::kInStack, 80);
   stream.AddDexRegisterEntry(Kind::kConstant, 10);
   stream.AddDexRegisterEntry(Kind::kInRegister, 5);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(52, 5, kVirtual, 0);
+  stream.BeginInlineInfoEntry(&art_method, 5, 0);
   stream.EndInlineInfoEntry();
 
   stream.EndStackMapEntry();
@@ -712,12 +713,12 @@
   stream.AddDexRegisterEntry(Kind::kInStack, 56);
   stream.AddDexRegisterEntry(Kind::kConstant, 0);
 
-  stream.BeginInlineInfoEntry(42, 2, kVirtual, 0);
+  stream.BeginInlineInfoEntry(&art_method, 2, 0);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(52, 5, kInterface, 1);
+  stream.BeginInlineInfoEntry(&art_method, 5, 1);
   stream.AddDexRegisterEntry(Kind::kInRegister, 2);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(52, 10, kStatic, 2);
+  stream.BeginInlineInfoEntry(&art_method, 10, 2);
   stream.AddDexRegisterEntry(Kind::kNone, 0);
   stream.AddDexRegisterEntry(Kind::kInRegister, 3);
   stream.EndInlineInfoEntry();
@@ -743,11 +744,9 @@
     InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding);
     ASSERT_EQ(2u, if0.GetDepth(encoding.inline_info_encoding));
     ASSERT_EQ(2u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(42u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+    ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0));
     ASSERT_EQ(3u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(82u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+    ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1));
 
     DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, encoding, 1);
     ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
@@ -769,14 +768,11 @@
     InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding);
     ASSERT_EQ(3u, if1.GetDepth(encoding.inline_info_encoding));
     ASSERT_EQ(2u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(42u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(kDirect, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0));
     ASSERT_EQ(3u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(82u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(kStatic, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1));
     ASSERT_EQ(5u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 2));
-    ASSERT_EQ(52u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2));
-    ASSERT_EQ(kVirtual, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2));
+    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 2));
 
     DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, encoding, 1);
     ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
@@ -810,14 +806,11 @@
     InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding);
     ASSERT_EQ(3u, if2.GetDepth(encoding.inline_info_encoding));
     ASSERT_EQ(2u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(42u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(kVirtual, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0));
     ASSERT_EQ(5u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(kInterface, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1));
     ASSERT_EQ(10u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 2));
-    ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2));
-    ASSERT_EQ(kStatic, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2));
+    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 2));
 
     ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(encoding.inline_info_encoding, 0));
 
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 96976d9..9c20740 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -121,7 +121,7 @@
 inline uint32_t ArtMethod::GetDexMethodIndex() {
   DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() ||
          GetDeclaringClass()->IsErroneous());
-  return dex_method_index_;
+  return GetDexMethodIndexUnchecked();
 }
 
 inline ArtMethod** ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 11dcc35..2c31f6c 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -322,6 +322,9 @@
   // Number of 32bit registers that would be required to hold all the arguments
   static size_t NumArgRegisters(const StringPiece& shorty);
 
+  ALWAYS_INLINE uint32_t GetDexMethodIndexUnchecked() {
+    return dex_method_index_;
+  }
   ALWAYS_INLINE uint32_t GetDexMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetDexMethodIndex(uint32_t new_idx) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c9b2cc8..8220ba4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2700,10 +2700,6 @@
   CHECK(h_new_class.Get() != nullptr) << descriptor;
   CHECK(h_new_class->IsResolved()) << descriptor;
 
-  // Update the dex cache of where the class is defined. Inlining depends on having
-  // this filled.
-  h_new_class->GetDexCache()->SetResolvedType(h_new_class->GetDexTypeIndex(), h_new_class.Get());
-
   // Instrumentation may have updated entrypoints for all methods of all
   // classes. However it could not update methods of this class while we
   // were loading it. Now the class is resolved, we can update entrypoints
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 42108d8..8518b08 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -899,7 +899,6 @@
   dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_;
   ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache();
   const DexFile& dex_file = klass->GetDexFile();
-  EXPECT_OBJ_PTR_EQ(dex_cache->GetResolvedType(type_idx), klass);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()),
       klass);
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 14c9c21..72dad0c 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -52,21 +52,19 @@
   // suspended while executing it.
   ScopedAssertNoThreadSuspension sants(__FUNCTION__);
 
+  if (inline_info.EncodesArtMethodAtDepth(encoding, inlining_depth)) {
+    return inline_info.GetArtMethodAtDepth(encoding, inlining_depth);
+  }
+
   uint32_t method_index = inline_info.GetMethodIndexAtDepth(encoding, inlining_depth);
-  InvokeType invoke_type = static_cast<InvokeType>(
-        inline_info.GetInvokeTypeAtDepth(encoding, inlining_depth));
-  ArtMethod* inlined_method = outer_method->GetDexCacheResolvedMethod(method_index,
-                                                                      kRuntimePointerSize);
-  if (!inlined_method->IsRuntimeMethod()) {
+  if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast<uint32_t>(-1)) {
+    // "charAt" special case. It is the only non-leaf method we inline across dex files.
+    ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
+    DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index);
     return inlined_method;
   }
 
-  // The method in the dex cache is the runtime method responsible for invoking
-  // the stub that will then update the dex cache. Therefore, we need to do the
-  // resolution ourselves.
-
-  // We first find the dex cache of our caller. If it is the outer method, we can directly
-  // use its dex cache. Otherwise, we also need to resolve our caller.
+  // Find which method did the call in the inlining hierarchy.
   ArtMethod* caller = outer_method;
   if (inlining_depth != 0) {
     caller = GetResolvedMethod(outer_method,
@@ -74,51 +72,38 @@
                                encoding,
                                inlining_depth - 1);
   }
-  DCHECK_EQ(caller->GetDexCache(), outer_method->GetDexCache())
-      << "Compiler only supports inlining calls within the same dex cache";
-  const DexFile* dex_file = outer_method->GetDexFile();
-  const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index);
 
-  if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast<uint32_t>(-1)) {
-    // "charAt" special case. It is the only non-leaf method we inline across dex files.
-    if (kIsDebugBuild) {
-      const char* name = dex_file->StringDataByIdx(method_id.name_idx_);
-      DCHECK_EQ(std::string(name), "charAt");
-      DCHECK_EQ(std::string(dex_file->GetMethodShorty(method_id)), "CI")
-          << std::string(dex_file->GetMethodShorty(method_id));
-      DCHECK_EQ(std::string(dex_file->StringByTypeIdx(method_id.class_idx_)), "Ljava/lang/String;")
-          << std::string(dex_file->StringByTypeIdx(method_id.class_idx_));
-    }
-    mirror::Class* cls =
-        Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangString);
-    // Update the dex cache for future lookups.
-    caller->GetDexCache()->SetResolvedType(method_id.class_idx_, cls);
-    inlined_method = cls->FindVirtualMethod("charAt", "(I)C", kRuntimePointerSize);
-  } else {
-    mirror::Class* klass = caller->GetDexCache()->GetResolvedType(method_id.class_idx_);
-    DCHECK_EQ(klass->GetDexCache(), caller->GetDexCache())
-        << "Compiler only supports inlining calls within the same dex cache";
-    switch (invoke_type) {
-      case kDirect:
-      case kStatic:
-        inlined_method =
-            klass->FindDirectMethod(klass->GetDexCache(), method_index, kRuntimePointerSize);
-        break;
-      case kSuper:
-      case kVirtual:
-        inlined_method =
-            klass->FindVirtualMethod(klass->GetDexCache(), method_index, kRuntimePointerSize);
-        break;
-      default:
-        LOG(FATAL) << "Unimplemented inlined invocation type: " << invoke_type;
-        UNREACHABLE();
+  // Lookup the declaring class of the inlined method.
+  const DexFile* dex_file = caller->GetDexFile();
+  const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index);
+  const char* descriptor = dex_file->StringByTypeIdx(method_id.class_idx_);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Thread* self = Thread::Current();
+  mirror::ClassLoader* class_loader = caller->GetDeclaringClass()->GetClassLoader();
+  mirror::Class* klass = class_linker->LookupClass(self, descriptor, class_loader);
+  if (klass == nullptr) {
+      LOG(FATAL) << "Could not find an inlined method from an .oat file: "
+                 << "the class " << descriptor << " was not found in the class loader of "
+                 << caller->PrettyMethod() << ". "
+                 << "This must be due to playing wrongly with class loaders";
+  }
+
+  // Lookup the method.
+  const char* method_name = dex_file->GetMethodName(method_id);
+  const Signature signature = dex_file->GetMethodSignature(method_id);
+
+  ArtMethod* inlined_method =
+      klass->FindDeclaredDirectMethod(method_name, signature, kRuntimePointerSize);
+  if (inlined_method == nullptr) {
+    inlined_method = klass->FindDeclaredVirtualMethod(method_name, signature, kRuntimePointerSize);
+    if (inlined_method == nullptr) {
+      LOG(FATAL) << "Could not find an inlined method from an .oat file: "
+                 << "the class " << descriptor << " does not have "
+                 << method_name << signature << " declared. "
+                 << "This must be due to duplicate classes or playing wrongly with class loaders";
     }
   }
 
-  // Update the dex cache for future lookups. Note that for static methods, this is safe
-  // when the class is being initialized, as the entrypoint for the ArtMethod is at
-  // this point still the resolution trampoline.
-  outer_method->SetDexCacheResolvedMethod(method_index, inlined_method, kRuntimePointerSize);
   return inlined_method;
 }
 
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index a7e7c21..9ebf9a7 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -18,8 +18,9 @@
 
 #include <stdint.h>
 
+#include "art_method.h"
 #include "indenter.h"
-#include "invoke_type.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -106,7 +107,7 @@
       << "InlineInfoEncoding"
       << " (method_index_bit_offset=" << static_cast<uint32_t>(kMethodIndexBitOffset)
       << ", dex_pc_bit_offset=" << static_cast<uint32_t>(dex_pc_bit_offset_)
-      << ", invoke_type_bit_offset=" << static_cast<uint32_t>(invoke_type_bit_offset_)
+      << ", extra_data_bit_offset=" << static_cast<uint32_t>(extra_data_bit_offset_)
       << ", dex_register_map_bit_offset=" << static_cast<uint32_t>(dex_register_map_bit_offset_)
       << ", total_bit_size=" << static_cast<uint32_t>(total_bit_size_)
       << ")\n";
@@ -230,12 +231,16 @@
     vios->Stream()
         << " At depth " << i
         << std::hex
-        << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i)
-        << std::dec
-        << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, i)
-        << ", invoke_type=" << static_cast<InvokeType>(GetInvokeTypeAtDepth(inline_info_encoding,
-                                                                            i))
-        << ")\n";
+        << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i);
+    if (EncodesArtMethodAtDepth(inline_info_encoding, i)) {
+      ScopedObjectAccess soa(Thread::Current());
+      vios->Stream() << ", method=" << GetArtMethodAtDepth(inline_info_encoding, i)->PrettyMethod();
+    } else {
+      vios->Stream()
+          << std::dec
+          << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, i);
+    }
+    vios->Stream() << ")\n";
     if (HasDexRegisterMapAtDepth(inline_info_encoding, i) && (number_of_dex_registers != nullptr)) {
       CodeInfoEncoding encoding = code_info.ExtractEncoding();
       DexRegisterMap dex_register_map =
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 5e556be..15d7816 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -35,6 +35,7 @@
 // Size of Dex virtual registers.
 static constexpr size_t kVRegSize = 4;
 
+class ArtMethod;
 class CodeInfo;
 class StackMapEncoding;
 struct CodeInfoEncoding;
@@ -887,7 +888,7 @@
  public:
   void SetFromSizes(size_t method_index_max,
                     size_t dex_pc_max,
-                    size_t invoke_type_max,
+                    size_t extra_data_max,
                     size_t dex_register_map_size) {
     total_bit_size_ = kMethodIndexBitOffset;
     total_bit_size_ += MinimumBitsToStore(method_index_max);
@@ -899,8 +900,8 @@
       total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max);
     }
 
-    invoke_type_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
-    total_bit_size_ += MinimumBitsToStore(invoke_type_max);
+    extra_data_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
+    total_bit_size_ += MinimumBitsToStore(extra_data_max);
 
     // We also need +1 for kNoDexRegisterMap, but since the size is strictly
     // greater than any offset we might try to encode, we already implicitly have it.
@@ -912,10 +913,10 @@
     return FieldEncoding(kMethodIndexBitOffset, dex_pc_bit_offset_);
   }
   ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const {
-    return FieldEncoding(dex_pc_bit_offset_, invoke_type_bit_offset_, -1 /* min_value */);
+    return FieldEncoding(dex_pc_bit_offset_, extra_data_bit_offset_, -1 /* min_value */);
   }
-  ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const {
-    return FieldEncoding(invoke_type_bit_offset_, dex_register_map_bit_offset_);
+  ALWAYS_INLINE FieldEncoding GetExtraDataEncoding() const {
+    return FieldEncoding(extra_data_bit_offset_, dex_register_map_bit_offset_);
   }
   ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const {
     return FieldEncoding(dex_register_map_bit_offset_, total_bit_size_, -1 /* min_value */);
@@ -930,7 +931,7 @@
   static constexpr uint8_t kIsLastBitOffset = 0;
   static constexpr uint8_t kMethodIndexBitOffset = 1;
   uint8_t dex_pc_bit_offset_;
-  uint8_t invoke_type_bit_offset_;
+  uint8_t extra_data_bit_offset_;
   uint8_t dex_register_map_bit_offset_;
   uint8_t total_bit_size_;
 };
@@ -938,7 +939,11 @@
 /**
  * Inline information for a specific PC. The information is of the form:
  *
- *   [is_last, method_index, dex_pc, invoke_type, dex_register_map_offset]+.
+ *   [is_last,
+ *    method_index (or ArtMethod high bits),
+ *    dex_pc,
+ *    extra_data (ArtMethod low bits or 1),
+ *    dex_register_map_offset]+.
  */
 class InlineInfo {
  public:
@@ -960,6 +965,7 @@
 
   ALWAYS_INLINE uint32_t GetMethodIndexAtDepth(const InlineInfoEncoding& encoding,
                                                uint32_t depth) const {
+    DCHECK(!EncodesArtMethodAtDepth(encoding, depth));
     return encoding.GetMethodIndexEncoding().Load(GetRegionAtDepth(encoding, depth));
   }
 
@@ -980,15 +986,28 @@
     encoding.GetDexPcEncoding().Store(GetRegionAtDepth(encoding, depth), dex_pc);
   }
 
-  ALWAYS_INLINE uint32_t GetInvokeTypeAtDepth(const InlineInfoEncoding& encoding,
-                                              uint32_t depth) const {
-    return encoding.GetInvokeTypeEncoding().Load(GetRegionAtDepth(encoding, depth));
+  ALWAYS_INLINE bool EncodesArtMethodAtDepth(const InlineInfoEncoding& encoding,
+                                             uint32_t depth) const {
+    return (encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)) & 1) == 0;
   }
 
-  ALWAYS_INLINE void SetInvokeTypeAtDepth(const InlineInfoEncoding& encoding,
-                                          uint32_t depth,
-                                          uint32_t invoke_type) {
-    encoding.GetInvokeTypeEncoding().Store(GetRegionAtDepth(encoding, depth), invoke_type);
+  ALWAYS_INLINE void SetExtraDataAtDepth(const InlineInfoEncoding& encoding,
+                                         uint32_t depth,
+                                         uint32_t extra_data) {
+    encoding.GetExtraDataEncoding().Store(GetRegionAtDepth(encoding, depth), extra_data);
+  }
+
+  ALWAYS_INLINE ArtMethod* GetArtMethodAtDepth(const InlineInfoEncoding& encoding,
+                                               uint32_t depth) const {
+    uint32_t low_bits = encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth));
+    uint32_t high_bits = encoding.GetMethodIndexEncoding().Load(GetRegionAtDepth(encoding, depth));
+    if (high_bits == 0) {
+      return reinterpret_cast<ArtMethod*>(low_bits);
+    } else {
+      uint64_t address = high_bits;
+      address = address << 32;
+      return reinterpret_cast<ArtMethod*>(address | low_bits);
+    }
   }
 
   ALWAYS_INLINE uint32_t GetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding,
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index a5b275c..507ea16 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -103,6 +103,7 @@
 jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
 jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
 jmethodID WellKnownClasses::java_lang_Short_valueOf;
+jmethodID WellKnownClasses::java_lang_String_charAt;
 jmethodID WellKnownClasses::java_lang_System_runFinalization = nullptr;
 jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
 jmethodID WellKnownClasses::java_lang_Thread_init;
@@ -337,6 +338,7 @@
   java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V");
 
   java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
+  java_lang_String_charAt = CacheMethod(env, java_lang_String, false, "charAt", "(I)C");
   java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
   java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
   java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 371be61..b3ce3d1 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -113,6 +113,7 @@
   static jmethodID java_lang_reflect_Proxy_invoke;
   static jmethodID java_lang_Runtime_nativeLoad;
   static jmethodID java_lang_Short_valueOf;
+  static jmethodID java_lang_String_charAt;
   static jmethodID java_lang_System_runFinalization;
   static jmethodID java_lang_Thread_dispatchUncaughtException;
   static jmethodID java_lang_Thread_init;