Revert "Remove ShadowFrame::dex_pc_ (but keep dex_pc_ptr_)"

This reverts commit 50bc8fb89c79874e731f245abda3b9e48a541cfe.

Reason for revert: Performance regression

Change-Id: Ib39ceb9e1f2753c518dd24fd193a824211fd5795
diff --git a/oatdump/oatdump_app_test.cc b/oatdump/oatdump_app_test.cc
index bbd0e3d..4490647 100644
--- a/oatdump/oatdump_app_test.cc
+++ b/oatdump/oatdump_app_test.cc
@@ -19,27 +19,27 @@
 namespace art {
 
 TEST_F(OatDumpTest, TestAppWithBootImage) {
-  ASSERT_TRUE(GenerateAppOdexFile(kDynamicLinking, {"--runtime-arg", "-Xmx64M"}));
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeOatWithBootImage, {}, kListAndCode));
+  ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"}));
+  ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode));
 }
 TEST_F(OatDumpTest, TestAppWithBootImageStatic) {
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
-  ASSERT_TRUE(GenerateAppOdexFile(kStaticLinking, {"--runtime-arg", "-Xmx64M"}));
-  ASSERT_TRUE(Exec(kStaticLinking, kModeOatWithBootImage, {}, kListAndCode));
+  ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"}));
+  ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode));
 }
 
 TEST_F(OatDumpTest, TestAppImageWithBootImage) {
   TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS();  // GC bug, b/126305867
   const std::string app_image_arg = "--app-image-file=" + GetAppImageName();
-  ASSERT_TRUE(GenerateAppOdexFile(kDynamicLinking, {"--runtime-arg", "-Xmx64M", app_image_arg}));
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeAppImage, {}, kListAndCode));
+  ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M", app_image_arg}));
+  ASSERT_TRUE(Exec(kDynamic, kModeAppImage, {}, kListAndCode));
 }
 TEST_F(OatDumpTest, TestAppImageWithBootImageStatic) {
   TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS();  // GC bug, b/126305867
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   const std::string app_image_arg = "--app-image-file=" + GetAppImageName();
-  ASSERT_TRUE(GenerateAppOdexFile(kStaticLinking, {"--runtime-arg", "-Xmx64M", app_image_arg}));
-  ASSERT_TRUE(Exec(kStaticLinking, kModeAppImage, {}, kListAndCode));
+  ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M", app_image_arg}));
+  ASSERT_TRUE(Exec(kStatic, kModeAppImage, {}, kListAndCode));
 }
 
 }  // namespace art
diff --git a/oatdump/oatdump_image_test.cc b/oatdump/oatdump_image_test.cc
index 6f6b83b..0a076f0 100644
--- a/oatdump/oatdump_image_test.cc
+++ b/oatdump/oatdump_image_test.cc
@@ -28,25 +28,25 @@
 TEST_F(OatDumpTest, TestImage) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeArt, {}, kListAndCode));
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {}, kListAndCode));
 }
 TEST_F(OatDumpTest, TestImageStatic) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kStaticLinking, kModeArt, {}, kListAndCode));
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {}, kListAndCode));
 }
 
 TEST_F(OatDumpTest, TestOatImage) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeCoreOat, {}, kListAndCode));
+  ASSERT_TRUE(Exec(kDynamic, kModeCoreOat, {}, kListAndCode));
 }
 TEST_F(OatDumpTest, TestOatImageStatic) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kStaticLinking, kModeCoreOat, {}, kListAndCode));
+  ASSERT_TRUE(Exec(kStatic, kModeCoreOat, {}, kListAndCode));
 }
 
 }  // namespace art
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index 87f9ac6..506fde7 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -30,61 +30,61 @@
 TEST_F(OatDumpTest, TestNoDumpVmap) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeArt, {"--no-dump:vmap"}, kListAndCode));
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-dump:vmap"}, kListAndCode));
 }
 TEST_F(OatDumpTest, TestNoDumpVmapStatic) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kStaticLinking, kModeArt, {"--no-dump:vmap"}, kListAndCode));
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-dump:vmap"}, kListAndCode));
 }
 
 TEST_F(OatDumpTest, TestNoDisassemble) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeArt, {"--no-disassemble"}, kListAndCode));
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-disassemble"}, kListAndCode));
 }
 TEST_F(OatDumpTest, TestNoDisassembleStatic) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kStaticLinking, kModeArt, {"--no-disassemble"}, kListAndCode));
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-disassemble"}, kListAndCode));
 }
 
 TEST_F(OatDumpTest, TestListClasses) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeArt, {"--list-classes"}, kListOnly));
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-classes"}, kListOnly));
 }
 TEST_F(OatDumpTest, TestListClassesStatic) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kStaticLinking, kModeArt, {"--list-classes"}, kListOnly));
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-classes"}, kListOnly));
 }
 
 TEST_F(OatDumpTest, TestListMethods) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeArt, {"--list-methods"}, kListOnly));
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-methods"}, kListOnly));
 }
 TEST_F(OatDumpTest, TestListMethodsStatic) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kStaticLinking, kModeArt, {"--list-methods"}, kListOnly));
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-methods"}, kListOnly));
 }
 
 TEST_F(OatDumpTest, TestSymbolize) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeSymbolize, {}, kListOnly));
+  ASSERT_TRUE(Exec(kDynamic, kModeSymbolize, {}, kListOnly));
 }
 TEST_F(OatDumpTest, TestSymbolizeStatic) {
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(Exec(kStaticLinking, kModeSymbolize, {}, kListOnly));
+  ASSERT_TRUE(Exec(kStatic, kModeSymbolize, {}, kListOnly));
 }
 
 TEST_F(OatDumpTest, TestExportDex) {
@@ -92,8 +92,8 @@
   // Test is failing on target, b/77469384.
   TEST_DISABLED_FOR_TARGET();
   std::string error_msg;
-  ASSERT_TRUE(GenerateAppOdexFile(kDynamicLinking, {"--runtime-arg", "-Xmx64M"}));
-  ASSERT_TRUE(Exec(kDynamicLinking, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
+  ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"}));
+  ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
   const std::string dex_location =
       tmp_dir_+ "/" + android::base::Basename(GetTestDexFileName(GetAppBaseName().c_str())) +
       "_export.dex";
@@ -109,8 +109,8 @@
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(GenerateAppOdexFile(kStaticLinking, {"--runtime-arg", "-Xmx64M"}));
-  ASSERT_TRUE(Exec(kStaticLinking, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
+  ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"}));
+  ASSERT_TRUE(Exec(kStatic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
 }
 
 }  // namespace art
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index 997321f..359b060 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -66,8 +66,8 @@
 
   // Linking flavor.
   enum Flavor {
-    kDynamicLinking,  // oatdump(d), dex2oat(d)
-    kStaticLinking,   // oatdump(d)s, dex2oat(d)s
+    kDynamic,  // oatdump(d), dex2oat(d)
+    kStatic,   // oatdump(d)s, dex2oat(d)s
   };
 
   // Returns path to the oatdump/dex2oat/dexdump binary.
@@ -83,7 +83,7 @@
   }
 
   std::string GetExecutableFilePath(Flavor flavor, const char* name) {
-    return GetExecutableFilePath(name, kIsDebugBuild, flavor == kStaticLinking);
+    return GetExecutableFilePath(name, kIsDebugBuild, flavor == kStatic);
   }
 
   enum Mode {
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index d22771d..fc3005d 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -1917,7 +1917,6 @@
   self->VerifyStack();
 
   uint32_t dex_pc = shadow_frame.GetDexPC();
-  DCHECK_LT(dex_pc, shadow_frame.GetMethod()->DexInstructionData().InsnsSizeInCodeUnits());
   const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
   const uint16_t* const insns = accessor.Insns();
   const Instruction* next = Instruction::At(insns + dex_pc);
diff --git a/runtime/interpreter/mterp/arm/main.S b/runtime/interpreter/mterp/arm/main.S
index 95bc864..12b3dab 100644
--- a/runtime/interpreter/mterp/arm/main.S
+++ b/runtime/interpreter/mterp/arm/main.S
@@ -109,10 +109,12 @@
  */
 #define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
 #define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
 #define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
 #define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
 #define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
 #define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
 #define OFF_FP_SHADOWFRAME OFF_FP(0)
 
 /*
@@ -120,11 +122,25 @@
  * be done *before* something throws.
  *
  * It's okay to do this more than once.
+ *
+ * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped
+ * dex byte codes.  However, the rest of the runtime expects dex pc to be an instruction
+ * offset into the code_items_[] array.  For effiency, we will "export" the
+ * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC
+ * to convert to a dex pc when needed.
  */
 .macro EXPORT_PC
     str  rPC, [rFP, #OFF_FP_DEX_PC_PTR]
 .endm
 
+.macro EXPORT_DEX_PC tmp
+    ldr  \tmp, [rFP, #OFF_FP_DEX_INSTRUCTIONS]
+    str  rPC, [rFP, #OFF_FP_DEX_PC_PTR]
+    sub  \tmp, rPC, \tmp
+    asr  \tmp, #1
+    str  \tmp, [rFP, #OFF_FP_DEX_PC]
+.endm
+
 /*
  * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
  */
@@ -382,13 +398,18 @@
     /* Remember the return register */
     str     r3, [r2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
 
+    /* Remember the dex instruction pointer */
+    str     r1, [r2, #SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET]
+
     /* set up "named" registers */
     mov     rSELF, r0
     ldr     r0, [r2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET]
-    add     rFP, r2, #SHADOWFRAME_VREGS_OFFSET         @ pointer to vregs.
-    VREG_INDEX_TO_ADDR rREFS, r0                       @ pointer to reference array in shadow frame
-    ldr     rPC, [r2, #SHADOWFRAME_DEX_PC_PTR_OFFSET]  @ pointer to current dex instruction.
+    add     rFP, r2, #SHADOWFRAME_VREGS_OFFSET     @ point to vregs.
+    VREG_INDEX_TO_ADDR rREFS, r0                   @ point to reference array in shadow frame
+    ldr     r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET]   @ Get starting dex_pc.
+    add     rPC, r1, r0, lsl #1                    @ Create direct pointer to 1st dex opcode
     CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
+    EXPORT_PC
 
     /* Starting ibase */
     ldr     rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
@@ -530,8 +551,10 @@
     bl      MterpHandleException                    @ (self, shadow_frame)
     cmp     r0, #0
     beq     MterpExceptionReturn                    @ no local catch, back to caller.
+    ldr     r0, [rFP, #OFF_FP_DEX_INSTRUCTIONS]
+    ldr     r1, [rFP, #OFF_FP_DEX_PC]
     ldr     rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
-    ldr     rPC, [rFP, #OFF_FP_DEX_PC_PTR]          @ reload dex pc pointer.
+    add     rPC, r0, r1, lsl #1                     @ generate new dex_pc_ptr
     /* Do we need to switch interpreters? */
     ldr     r0, [rSELF, #THREAD_USE_MTERP_OFFSET]
     cmp     r0, #0
diff --git a/runtime/interpreter/mterp/arm64/main.S b/runtime/interpreter/mterp/arm64/main.S
index e16b51c..fd745f1 100644
--- a/runtime/interpreter/mterp/arm64/main.S
+++ b/runtime/interpreter/mterp/arm64/main.S
@@ -115,10 +115,12 @@
  */
 #define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
 #define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
 #define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
 #define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
 #define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
 #define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
 #define OFF_FP_SHADOWFRAME OFF_FP(0)
 
 /*
@@ -126,6 +128,12 @@
  * be done *before* something throws.
  *
  * It's okay to do this more than once.
+ *
+ * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped
+ * dex byte codes.  However, the rest of the runtime expects dex pc to be an instruction
+ * offset into the code_items_[] array.  For effiency, we will "export" the
+ * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC
+ * to convert to a dex pc when needed.
  */
 .macro EXPORT_PC
     str  xPC, [xFP, #OFF_FP_DEX_PC_PTR]
@@ -403,13 +411,18 @@
     /* Remember the return register */
     str     x3, [x2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
 
+    /* Remember the dex instruction pointer */
+    str     x1, [x2, #SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET]
+
     /* set up "named" registers */
     mov     xSELF, x0
     ldr     w0, [x2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET]
-    add     xFP, x2, #SHADOWFRAME_VREGS_OFFSET         // pointer to vregs.
-    add     xREFS, xFP, w0, uxtw #2                    // pointer to reference array in shadow frame
-    ldr     xPC, [x2, #SHADOWFRAME_DEX_PC_PTR_OFFSET]  // pointer to current dex instruction.
+    add     xFP, x2, #SHADOWFRAME_VREGS_OFFSET     // point to vregs.
+    add     xREFS, xFP, w0, uxtw #2                // point to reference array in shadow frame
+    ldr     w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET]   // Get starting dex_pc.
+    add     xPC, x1, w0, uxtw #1                   // Create direct pointer to 1st dex opcode
     CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
+    EXPORT_PC
 
     /* Starting ibase */
     ldr     xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET]
@@ -548,8 +561,10 @@
     add     x1, xFP, #OFF_FP_SHADOWFRAME
     bl      MterpHandleException                    // (self, shadow_frame)
     cbz     w0, MterpExceptionReturn                // no local catch, back to caller.
+    ldr     x0, [xFP, #OFF_FP_DEX_INSTRUCTIONS]
+    ldr     w1, [xFP, #OFF_FP_DEX_PC]
     ldr     xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET]
-    ldr     xPC, [xFP, #OFF_FP_DEX_PC_PTR]          // reload dex pc pointer.
+    add     xPC, x0, x1, lsl #1                     // generate new dex_pc_ptr
     /* Do we need to switch interpreters? */
     ldr     w0, [xSELF, #THREAD_USE_MTERP_OFFSET]
     cbz     w0, MterpFallback
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 57ebfac..80ebf21 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -554,10 +554,14 @@
   }
   DCHECK(!Runtime::Current()->IsActiveTransaction());
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  DCHECK_EQ(self->IsExceptionPending(), inst->Opcode() == Instruction::MOVE_EXCEPTION);
-  uint32_t dex_pc = dex_pc_ptr - shadow_frame->GetDexInstructions();
-  DCHECK_LT(dex_pc, shadow_frame->GetMethod()->DexInstructionData().InsnsSizeInCodeUnits());
+  uint16_t inst_data = inst->Fetch16(0);
+  if (inst->Opcode(inst_data) == Instruction::MOVE_EXCEPTION) {
+    self->AssertPendingException();
+  } else {
+    self->AssertNoPendingException();
+  }
   if (kTraceExecutionEnabled) {
+    uint32_t dex_pc = dex_pc_ptr - shadow_frame->GetDexInstructions();
     TraceExecution(*shadow_frame, inst, dex_pc);
   }
   if (kTestExportPC) {
diff --git a/runtime/interpreter/mterp/mterp.h b/runtime/interpreter/mterp/mterp.h
index d8fe358..af52758 100644
--- a/runtime/interpreter/mterp/mterp.h
+++ b/runtime/interpreter/mterp/mterp.h
@@ -40,8 +40,8 @@
 // handler for a recent opcode failed to export the Dalvik PC prior to a possible exit from
 // the mterp environment.
 constexpr uintptr_t kExportPCPoison = 0xdead00ff;
-// Set true to enable poison testing of ExportPC.
-constexpr bool kTestExportPC = true;
+// Set true to enable poison testing of ExportPC.  Uses Alt interpreter.
+constexpr bool kTestExportPC = false;
 
 constexpr size_t kMterpHandlerSize = 128;
 
diff --git a/runtime/interpreter/mterp/x86/main.S b/runtime/interpreter/mterp/x86/main.S
index 03cbf49..8df75d2 100644
--- a/runtime/interpreter/mterp/x86/main.S
+++ b/runtime/interpreter/mterp/x86/main.S
@@ -131,10 +131,12 @@
  */
 #define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
 #define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
 #define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
 #define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
 #define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
 #define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
 #define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
 #define OFF_FP_SHADOWFRAME OFF_FP(0)
 
@@ -180,6 +182,12 @@
  * be done *before* something throws.
  *
  * It's okay to do this more than once.
+ *
+ * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped
+ * dex byte codes.  However, the rest of the runtime expects dex pc to be an instruction
+ * offset into the code_items_[] array.  For effiency, we will "export" the
+ * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC
+ * to convert to a dex pc when needed.
  */
 .macro EXPORT_PC
     movl    rPC, OFF_FP_DEX_PC_PTR(rFP)
@@ -385,12 +393,18 @@
     movl    IN_ARG3(%esp), %eax
     movl    %eax, SHADOWFRAME_RESULT_REGISTER_OFFSET(%edx)
 
+    /* Remember the code_item */
+    movl    IN_ARG1(%esp), %ecx
+    movl    %ecx, SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET(%edx)
+
     /* set up "named" registers */
     movl    SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(%edx), %eax
     leal    SHADOWFRAME_VREGS_OFFSET(%edx), rFP
     leal    (rFP, %eax, 4), rREFS
-    movl    SHADOWFRAME_DEX_PC_PTR_OFFSET(%edx), rPC
+    movl    SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax
+    lea     (%ecx, %eax, 2), rPC
     CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
+    EXPORT_PC
 
     /* Set up for backwards branches & osr profiling */
     movl    OFF_FP_METHOD(rFP), %eax
@@ -556,7 +570,10 @@
     call    SYMBOL(MterpHandleException)
     testb   %al, %al
     jz      MterpExceptionReturn
-    movl    OFF_FP_DEX_PC_PTR(rFP), rPC
+    movl    OFF_FP_DEX_INSTRUCTIONS(rFP), %eax
+    movl    OFF_FP_DEX_PC(rFP), %ecx
+    lea     (%eax, %ecx, 2), rPC
+    movl    rPC, OFF_FP_DEX_PC_PTR(rFP)
     /* Do we need to switch interpreters? */
     movl    rSELF, %eax
     cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
diff --git a/runtime/interpreter/mterp/x86_64/main.S b/runtime/interpreter/mterp/x86_64/main.S
index 11e4d61..5f1fd2d 100644
--- a/runtime/interpreter/mterp/x86_64/main.S
+++ b/runtime/interpreter/mterp/x86_64/main.S
@@ -127,10 +127,12 @@
  */
 #define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
 #define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
 #define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
 #define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
 #define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
 #define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
 #define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
 #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
 
@@ -181,6 +183,12 @@
  * be done *before* something throws.
  *
  * It's okay to do this more than once.
+ *
+ * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped
+ * dex byte codes.  However, the rest of the runtime expects dex pc to be an instruction
+ * offset into the code_items_[] array.  For effiency, we will "export" the
+ * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC
+ * to convert to a dex pc when needed.
  */
 .macro EXPORT_PC
     movq    rPC, OFF_FP_DEX_PC_PTR(rFP)
@@ -372,12 +380,17 @@
     /* Remember the return register */
     movq    IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2)
 
+    /* Remember the code_item */
+    movq    IN_ARG1, SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET(IN_ARG2)
+
     /* set up "named" registers */
     movl    SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax
     leaq    SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP
     leaq    (rFP, %rax, 4), rREFS
-    movq    SHADOWFRAME_DEX_PC_PTR_OFFSET(IN_ARG2), rPC
+    movl    SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax
+    leaq    (IN_ARG1, %rax, 2), rPC
     CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
+    EXPORT_PC
 
     /* Starting ibase */
     movq    IN_ARG0, rSELF
@@ -523,7 +536,10 @@
     call    SYMBOL(MterpHandleException)
     testb   %al, %al
     jz      MterpExceptionReturn
-    movq    OFF_FP_DEX_PC_PTR(rFP), rPC
+    movq    OFF_FP_DEX_INSTRUCTIONS(rFP), %rax
+    mov     OFF_FP_DEX_PC(rFP), %ecx
+    leaq    (%rax, %rcx, 2), rPC
+    movq    rPC, OFF_FP_DEX_PC_PTR(rFP)
     /* Do we need to switch interpreters? */
     movq    rSELF, %rax
     cmpb    LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index b0fa4a3..8cb2b33 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -21,10 +21,8 @@
 #include <cstring>
 #include <string>
 
-#include "art_method.h"
 #include "base/locks.h"
 #include "base/macros.h"
-#include "dex/code_item_accessors-inl.h"
 #include "lock_count_data.h"
 #include "read_barrier.h"
 #include "stack_reference.h"
@@ -104,7 +102,7 @@
   }
 
   uint32_t GetDexPC() const {
-    return (dex_pc_ptr_ == nullptr) ? dex::kDexNoIndex : (dex_pc_ptr_ - dex_instructions_);
+    return (dex_pc_ptr_ == nullptr) ? dex_pc_ : dex_pc_ptr_ - dex_instructions_;
   }
 
   int16_t GetCachedHotnessCountdown() const {
@@ -124,7 +122,8 @@
   }
 
   void SetDexPC(uint32_t dex_pc) {
-    dex_pc_ptr_ = (dex_pc == dex::kDexNoIndex) ? nullptr : (dex_instructions_ + dex_pc);
+    dex_pc_ = dex_pc;
+    dex_pc_ptr_ = nullptr;
   }
 
   ShadowFrame* GetLink() const {
@@ -281,6 +280,10 @@
     return OFFSETOF_MEMBER(ShadowFrame, method_);
   }
 
+  static constexpr size_t DexPCOffset() {
+    return OFFSETOF_MEMBER(ShadowFrame, dex_pc_);
+  }
+
   static constexpr size_t NumberOfVRegsOffset() {
     return OFFSETOF_MEMBER(ShadowFrame, number_of_vregs_);
   }
@@ -297,6 +300,10 @@
     return OFFSETOF_MEMBER(ShadowFrame, dex_pc_ptr_);
   }
 
+  static constexpr size_t DexInstructionsOffset() {
+    return OFFSETOF_MEMBER(ShadowFrame, dex_instructions_);
+  }
+
   static constexpr size_t CachedHotnessCountdownOffset() {
     return OFFSETOF_MEMBER(ShadowFrame, cached_hotness_countdown_);
   }
@@ -382,9 +389,10 @@
       : link_(link),
         method_(method),
         result_register_(nullptr),
-        dex_instructions_(method == nullptr ? nullptr : method->DexInstructionData().Insns()),
-        dex_pc_ptr_(dex_instructions_ + dex_pc),
+        dex_pc_ptr_(nullptr),
+        dex_instructions_(nullptr),
         number_of_vregs_(num_vregs),
+        dex_pc_(dex_pc),
         cached_hotness_countdown_(0),
         hotness_countdown_(0),
         frame_flags_(0) {
@@ -417,10 +425,12 @@
   ShadowFrame* link_;
   ArtMethod* method_;
   JValue* result_register_;
-  const uint16_t* dex_instructions_;  // Dex instruction base of the code item.
   const uint16_t* dex_pc_ptr_;
+  // Dex instruction base of the code item.
+  const uint16_t* dex_instructions_;
   LockCountData lock_count_data_;  // This may contain GC roots when lock counting is active.
   const uint32_t number_of_vregs_;
+  uint32_t dex_pc_;
   int16_t cached_hotness_countdown_;
   int16_t hotness_countdown_;
 
diff --git a/tools/cpp-define-generator/shadow_frame.def b/tools/cpp-define-generator/shadow_frame.def
index 5bf3bef..10a309c 100644
--- a/tools/cpp-define-generator/shadow_frame.def
+++ b/tools/cpp-define-generator/shadow_frame.def
@@ -20,6 +20,10 @@
 
 ASM_DEFINE(SHADOWFRAME_CACHED_HOTNESS_COUNTDOWN_OFFSET,
            art::ShadowFrame::CachedHotnessCountdownOffset())
+ASM_DEFINE(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET,
+           art::ShadowFrame::DexInstructionsOffset())
+ASM_DEFINE(SHADOWFRAME_DEX_PC_OFFSET,
+           art::ShadowFrame::DexPCOffset())
 ASM_DEFINE(SHADOWFRAME_DEX_PC_PTR_OFFSET,
            art::ShadowFrame::DexPCPtrOffset())
 ASM_DEFINE(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET,