[VM interpreter] Generation of pc descriptors, exception handlers for bytecode.
Fixes in stack unwinding and frame iteration.

Change-Id: Ic67eb5727512138cbfa9313094d48e21384b220c
Reviewed-on: https://dart-review.googlesource.com/59520
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 23c595e..b100719 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 #include "vm/compiler/frontend/kernel_binary_flowgraph.h"
+#include "vm/code_descriptors.h"
 #include "vm/compiler/aot/precompiler.h"
 #include "vm/compiler/assembler/disassembler_kbc.h"
 #include "vm/compiler/frontend/prologue_builder.h"
@@ -1168,7 +1169,7 @@
           elem = pool.ObjectAt(elem_index);
           array.SetAt(j, elem);
         }
-        obj = H.Canonicalize(Array::Cast(obj));
+        obj = H.Canonicalize(Array::Cast(array));
         ASSERT(!obj.IsNull());
       } break;
       case ConstantPoolTag::kInstance: {
@@ -1354,37 +1355,62 @@
 }
 
 void BytecodeMetadataHelper::ReadExceptionsTable(const Code& bytecode) {
-  const ObjectPool& pool =
-      ObjectPool::Handle(builder_->zone_, bytecode.object_pool());
-  AbstractType& handled_type = AbstractType::Handle(builder_->zone_);
+  const intptr_t try_block_count = builder_->reader_.ReadListLength();
+  if (try_block_count > 0) {
+    const ObjectPool& pool =
+        ObjectPool::Handle(builder_->zone_, bytecode.object_pool());
+    AbstractType& handler_type = AbstractType::Handle(builder_->zone_);
+    Array& handler_types = Array::Handle(builder_->zone_);
+    DescriptorList* pc_descriptors_list =
+        new (builder_->zone_) DescriptorList(64);
+    ExceptionHandlerList* exception_handlers_list =
+        new (builder_->zone_) ExceptionHandlerList();
 
-  // Encoding of ExceptionsTable is described in
-  // pkg/vm/lib/bytecode/exceptions.dart.
-  intptr_t try_block_count = builder_->reader_.ReadListLength();
-  for (intptr_t i = 0; i < try_block_count; i++) {
-    intptr_t outer_try_index_plus1 = builder_->reader_.ReadUInt();
-    intptr_t outer_try_index = outer_try_index_plus1 - 1;
-    USE(outer_try_index);
-    intptr_t start_pc = builder_->reader_.ReadUInt();
-    USE(start_pc);
-    intptr_t end_pc = builder_->reader_.ReadUInt();
-    USE(end_pc);
-    intptr_t handler_pc = builder_->reader_.ReadUInt();
-    USE(handler_pc);
-    uint8_t flags = builder_->reader_.ReadByte();
-    // flagNeedsStackTrace = 1 << 0;
-    // flagIsSynthetic = 1 << 1;
-    USE(flags);
+    // Encoding of ExceptionsTable is described in
+    // pkg/vm/lib/bytecode/exceptions.dart.
+    for (intptr_t try_index = 0; try_index < try_block_count; try_index++) {
+      intptr_t outer_try_index_plus1 = builder_->reader_.ReadUInt();
+      intptr_t outer_try_index = outer_try_index_plus1 - 1;
+      intptr_t start_pc = builder_->reader_.ReadUInt();
+      intptr_t end_pc = builder_->reader_.ReadUInt();
+      intptr_t handler_pc = builder_->reader_.ReadUInt();
+      uint8_t flags = builder_->reader_.ReadByte();
+      const uint8_t kFlagNeedsStackTrace = 1 << 0;
+      const uint8_t kFlagIsSynthetic = 1 << 1;
+      const bool needs_stacktrace = (flags & kFlagNeedsStackTrace) != 0;
+      const bool is_generated = (flags & kFlagIsSynthetic) != 0;
+      intptr_t type_count = builder_->reader_.ReadListLength();
+      ASSERT(type_count > 0);
+      handler_types = Array::New(type_count, Heap::kOld);
+      for (intptr_t i = 0; i < type_count; i++) {
+        intptr_t type_index = builder_->reader_.ReadUInt();
+        ASSERT(type_index < pool.Length());
+        handler_type ^= pool.ObjectAt(type_index);
+        handler_types.SetAt(i, handler_type);
+      }
+      pc_descriptors_list->AddDescriptor(RawPcDescriptors::kOther, start_pc,
+                                         Thread::kNoDeoptId,
+                                         TokenPosition::kNoSource, try_index);
+      pc_descriptors_list->AddDescriptor(RawPcDescriptors::kOther, end_pc,
+                                         Thread::kNoDeoptId,
+                                         TokenPosition::kNoSource, -1);
 
-    intptr_t type_count = builder_->reader_.ReadListLength();
-    for (intptr_t j = 0; j < type_count; j++) {
-      intptr_t type_index = builder_->reader_.ReadUInt();
-      ASSERT(type_index < pool.Length());
-      handled_type ^= pool.ObjectAt(type_index);
+      exception_handlers_list->AddHandler(
+          try_index, outer_try_index, handler_pc, TokenPosition::kNoSource,
+          is_generated, handler_types, needs_stacktrace);
     }
+    const PcDescriptors& descriptors = PcDescriptors::Handle(
+        builder_->zone_,
+        pc_descriptors_list->FinalizePcDescriptors(bytecode.PayloadStart()));
+    bytecode.set_pc_descriptors(descriptors);
+    const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
+        builder_->zone_, exception_handlers_list->FinalizeExceptionHandlers(
+                             bytecode.PayloadStart()));
+    bytecode.set_exception_handlers(handlers);
+  } else {
+    bytecode.set_pc_descriptors(Object::empty_descriptors());
+    bytecode.set_exception_handlers(Object::empty_exception_handlers());
   }
-  // TODO(regis): Generate exception handlers (as well as pc descriptors)
-  // and store in bytecode: bytecode.set_exception_handlers(exception_handlers);
 }
 #endif  // defined(DART_USE_INTERPRETER)
 
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index fdbc665..c61a2d5 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -841,6 +841,42 @@
   *SP = fp - kKBCDartFrameFixedSize;
 }
 
+// Note: functions below are marked DART_NOINLINE to recover performance on
+// ARM where inlining these functions into the interpreter loop seemed to cause
+// some code quality issues.
+static DART_NOINLINE bool InvokeRuntime(Thread* thread,
+                                        Interpreter* interpreter,
+                                        RuntimeFunction drt,
+                                        const NativeArguments& args) {
+  InterpreterSetjmpBuffer buffer(interpreter);
+  if (!setjmp(buffer.buffer_)) {
+    thread->set_vm_tag(reinterpret_cast<uword>(drt));
+    drt(args);
+    thread->set_vm_tag(VMTag::kDartTagId);
+    thread->set_top_exit_frame_info(0);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+static DART_NOINLINE bool InvokeNative(Thread* thread,
+                                       Interpreter* interpreter,
+                                       NativeFunctionWrapper wrapper,
+                                       Dart_NativeFunction function,
+                                       Dart_NativeArguments args) {
+  InterpreterSetjmpBuffer buffer(interpreter);
+  if (!setjmp(buffer.buffer_)) {
+    thread->set_vm_tag(reinterpret_cast<uword>(function));
+    wrapper(args, function);
+    thread->set_vm_tag(VMTag::kDartTagId);
+    thread->set_top_exit_frame_info(0);
+    return true;
+  } else {
+    return false;
+  }
+}
+
 DART_NOINLINE bool Interpreter::InvokeCompiled(Thread* thread,
                                                RawFunction* function,
                                                RawArray* argdesc,
@@ -849,64 +885,79 @@
                                                uint32_t** pc,
                                                RawObject*** FP,
                                                RawObject*** SP) {
-  InterpreterSetjmpBuffer buffer(this);
-  if (!setjmp(buffer.buffer_)) {
 #if defined(USING_SIMULATOR) || defined(TARGET_ARCH_DBC)
-    // TODO(regis): Revisit.
-    UNIMPLEMENTED();
+  // TODO(regis): Revisit.
+  UNIMPLEMENTED();
 #endif
-    ASSERT(thread->vm_tag() == VMTag::kDartTagId);
-    ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
-    if (!Function::HasCode(function)) {
-      ASSERT(!Function::HasBytecode(function));
-      call_top[1] = 0;  // Code result.
-      call_top[2] = function;
-      CallRuntime(thread, *FP, call_top + 3, *pc, 1, call_top + 2, call_top + 1,
-                  reinterpret_cast<uword>(DRT_CompileFunction));
+  if (!Function::HasCode(function)) {
+    ASSERT(!Function::HasBytecode(function));
+    call_top[1] = 0;  // Code result.
+    call_top[2] = function;
+    Exit(thread, *FP, call_top + 3, *pc);
+    NativeArguments native_args(thread, 1, call_top + 2, call_top + 1);
+    if (!InvokeRuntime(thread, this, DRT_CompileFunction, native_args)) {
+      return false;
     }
-    if (Function::HasCode(function)) {
-      RawCode* code = function->ptr()->code_;
-      ASSERT(code != StubCode::LazyCompile_entry()->code());
-      // TODO(regis): Once we share the same stack, try to invoke directly.
-
-      // On success, returns a RawInstance.  On failure, a RawError.
-      typedef RawObject* (*invokestub)(RawCode * code, RawArray * argdesc,
-                                       RawObject * *arg0, Thread * thread);
-      invokestub entrypoint = reinterpret_cast<invokestub>(
-          StubCode::InvokeDartCodeFromBytecode_entry()->EntryPoint());
-      RawObject* result = entrypoint(code, argdesc, call_base, thread);
-
-      // Pop args and push result.
-      *SP = call_base;
-      **SP = result;
-    } else {
-      ASSERT(Function::HasBytecode(function));
-      // Bytecode was loaded in the above compilation step.
-      // Stay in interpreter.
-      RawCode* bytecode = function->ptr()->bytecode_;
-      RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
-      callee_fp[kKBCPcMarkerSlotFromFp] = bytecode;
-      callee_fp[kKBCSavedCallerPcSlotFromFp] =
-          reinterpret_cast<RawObject*>(*pc);
-      callee_fp[kKBCSavedCallerFpSlotFromFp] =
-          reinterpret_cast<RawObject*>(*FP);
-      pp_ = bytecode->ptr()->object_pool_;
-      *pc = reinterpret_cast<uint32_t*>(bytecode->ptr()->entry_point_);
-      pc_ = reinterpret_cast<uword>(*pc);  // For the profiler.
-      *FP = callee_fp;
-      *SP = *FP - 1;
-      // Dispatch will interpret function.
-    }
-    ASSERT(thread->vm_tag() == VMTag::kDartTagId);
-    ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
-    thread->set_top_exit_frame_info(0);
-    return true;
-  } else {
-    return false;
   }
+  if (Function::HasCode(function)) {
+    RawCode* code = function->ptr()->code_;
+    ASSERT(code != StubCode::LazyCompile_entry()->code());
+    // TODO(regis): Once we share the same stack, try to invoke directly.
+
+    // On success, returns a RawInstance.  On failure, a RawError.
+    typedef RawObject* (*invokestub)(RawCode * code, RawArray * argdesc,
+                                     RawObject * *arg0, Thread * thread);
+    invokestub entrypoint = reinterpret_cast<invokestub>(
+        StubCode::InvokeDartCodeFromBytecode_entry()->EntryPoint());
+    Exit(thread, *FP, call_top + 1, *pc);
+    {
+      InterpreterSetjmpBuffer buffer(this);
+      if (!setjmp(buffer.buffer_)) {
+        thread->set_vm_tag(reinterpret_cast<uword>(entrypoint));
+        RawObject* result = entrypoint(code, argdesc, call_base, thread);
+        thread->set_vm_tag(VMTag::kDartTagId);
+        thread->set_top_exit_frame_info(0);
+        ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
+
+        // It is legit to call the constructor of an error object, however a
+        // result of class UnhandledException must be propagated.
+        if (result->IsHeapObject() &&
+            result->GetClassId() == kUnhandledExceptionCid) {
+          // TODO(regis): Shoudn't the callee have set these and thrown?
+          special_[kExceptionSpecialIndex] = result;
+          special_[kStackTraceSpecialIndex] = Object::null();
+          return false;
+        }
+
+        // Pop args and push result.
+        *SP = call_base;
+        **SP = result;
+        return true;
+      } else {
+        return false;
+      }
+    }
+  }
+  ASSERT(Function::HasBytecode(function));
+  // Bytecode was loaded in the above compilation step.
+  // Stay in interpreter.
+  RawCode* bytecode = function->ptr()->bytecode_;
+  RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
+  callee_fp[kKBCPcMarkerSlotFromFp] = bytecode;
+  callee_fp[kKBCSavedCallerPcSlotFromFp] = reinterpret_cast<RawObject*>(*pc);
+  callee_fp[kKBCSavedCallerFpSlotFromFp] = reinterpret_cast<RawObject*>(*FP);
+  pp_ = bytecode->ptr()->object_pool_;
+  *pc = reinterpret_cast<uint32_t*>(bytecode->ptr()->entry_point_);
+  pc_ = reinterpret_cast<uword>(*pc);  // For the profiler.
+  *FP = callee_fp;
+  *SP = *FP - 1;
+  // Dispatch will interpret function.
+  ASSERT(thread->vm_tag() == VMTag::kDartTagId);
+  ASSERT(thread->top_exit_frame_info() == 0);
+  return true;
 }
 
-DART_FORCE_INLINE void Interpreter::Invoke(Thread* thread,
+DART_FORCE_INLINE bool Interpreter::Invoke(Thread* thread,
                                            RawObject** call_base,
                                            RawObject** call_top,
                                            uint32_t** pc,
@@ -923,25 +974,19 @@
 #endif
   if (Function::HasCode(function) || !Function::HasBytecode(function)) {
     // TODO(regis): If the function is a dispatcher, execute the dispatch here.
-    if (!InvokeCompiled(thread, function, argdesc_, call_base, call_top, pc, FP,
-                        SP)) {
-      // Handle exception
-      *FP = reinterpret_cast<RawObject**>(fp_);
-      *pc = reinterpret_cast<uint32_t*>(pc_);
-      pp_ = InterpreterHelpers::FrameCode(*FP)->ptr()->object_pool_;
-      *SP = *FP - 1;
-    }
-  } else {
-    RawCode* bytecode = function->ptr()->bytecode_;
-    callee_fp[kKBCPcMarkerSlotFromFp] = bytecode;
-    callee_fp[kKBCSavedCallerPcSlotFromFp] = reinterpret_cast<RawObject*>(*pc);
-    callee_fp[kKBCSavedCallerFpSlotFromFp] = reinterpret_cast<RawObject*>(*FP);
-    pp_ = bytecode->ptr()->object_pool_;
-    *pc = reinterpret_cast<uint32_t*>(bytecode->ptr()->entry_point_);
-    pc_ = reinterpret_cast<uword>(*pc);  // For the profiler.
-    *FP = callee_fp;
-    *SP = *FP - 1;
+    return InvokeCompiled(thread, function, argdesc_, call_base, call_top, pc,
+                          FP, SP);
   }
+  RawCode* bytecode = function->ptr()->bytecode_;
+  callee_fp[kKBCPcMarkerSlotFromFp] = bytecode;
+  callee_fp[kKBCSavedCallerPcSlotFromFp] = reinterpret_cast<RawObject*>(*pc);
+  callee_fp[kKBCSavedCallerFpSlotFromFp] = reinterpret_cast<RawObject*>(*FP);
+  pp_ = bytecode->ptr()->object_pool_;
+  *pc = reinterpret_cast<uint32_t*>(bytecode->ptr()->entry_point_);
+  pc_ = reinterpret_cast<uword>(*pc);  // For the profiler.
+  *FP = callee_fp;
+  *SP = *FP - 1;
+  return true;
 }
 
 void Interpreter::InlineCacheMiss(int checked_args,
@@ -978,7 +1023,7 @@
               result, reinterpret_cast<uword>(handler));
 }
 
-DART_FORCE_INLINE void Interpreter::InstanceCall1(Thread* thread,
+DART_FORCE_INLINE bool Interpreter::InstanceCall1(Thread* thread,
                                                   RawICData* icdata,
                                                   RawObject** call_base,
                                                   RawObject** top,
@@ -1020,10 +1065,10 @@
                     *pc, *FP, *SP);
   }
 
-  Invoke(thread, call_base, top, pc, FP, SP);
+  return Invoke(thread, call_base, top, pc, FP, SP);
 }
 
-DART_FORCE_INLINE void Interpreter::InstanceCall2(Thread* thread,
+DART_FORCE_INLINE bool Interpreter::InstanceCall2(Thread* thread,
                                                   RawICData* icdata,
                                                   RawObject** call_base,
                                                   RawObject** top,
@@ -1068,7 +1113,7 @@
                     *pc, *FP, *SP);
   }
 
-  Invoke(thread, call_base, top, pc, FP, SP);
+  return Invoke(thread, call_base, top, pc, FP, SP);
 }
 
 DART_FORCE_INLINE void Interpreter::PrepareForTailCall(
@@ -1089,42 +1134,6 @@
   argdesc_ = args_desc;
 }
 
-// Note: functions below are marked DART_NOINLINE to recover performance on
-// ARM where inlining these functions into the interpreter loop seemed to cause
-// some code quality issues.
-static DART_NOINLINE bool InvokeRuntime(Thread* thread,
-                                        Interpreter* interpreter,
-                                        RuntimeFunction drt,
-                                        const NativeArguments& args) {
-  InterpreterSetjmpBuffer buffer(interpreter);
-  if (!setjmp(buffer.buffer_)) {
-    thread->set_vm_tag(reinterpret_cast<uword>(drt));
-    drt(args);
-    thread->set_vm_tag(VMTag::kDartTagId);
-    thread->set_top_exit_frame_info(0);
-    return true;
-  } else {
-    return false;
-  }
-}
-
-static DART_NOINLINE bool InvokeNative(Thread* thread,
-                                       Interpreter* interpreter,
-                                       NativeFunctionWrapper wrapper,
-                                       Dart_NativeFunction function,
-                                       Dart_NativeArguments args) {
-  InterpreterSetjmpBuffer buffer(interpreter);
-  if (!setjmp(buffer.buffer_)) {
-    thread->set_vm_tag(reinterpret_cast<uword>(function));
-    wrapper(args, function);
-    thread->set_vm_tag(VMTag::kDartTagId);
-    thread->set_top_exit_frame_info(0);
-    return true;
-  } else {
-    return false;
-  }
-}
-
 // Note:
 // All macro helpers are intended to be used only inside Interpreter::Call.
 
@@ -1965,7 +1974,9 @@
       RawObject** call_base = SP - argc;
       RawObject** call_top = SP;  // *SP contains function
       argdesc_ = static_cast<RawArray*>(LOAD_CONSTANT(rD));
-      Invoke(thread, call_base, call_top, &pc, &FP, &SP);
+      if (!Invoke(thread, call_base, call_top, &pc, &FP, &SP)) {
+        HANDLE_EXCEPTION;
+      }
     }
 
     DISPATCH();
@@ -1977,7 +1988,9 @@
     RawObject** call_base = SP - argc;
     RawObject** call_top = SP;  // *SP contains function
     argdesc_ = static_cast<RawArray*>(LOAD_CONSTANT(rD));
-    Invoke(thread, call_base, call_top, &pc, &FP, &SP);
+    if (!Invoke(thread, call_base, call_top, &pc, &FP, &SP)) {
+      HANDLE_EXCEPTION;
+    }
     DISPATCH();
   }
 
@@ -2001,8 +2014,10 @@
       RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
       InterpreterHelpers::IncrementUsageCounter(
           RAW_CAST(Function, icdata->ptr()->owner_));
-      InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP,
-                    false /* optimized */);
+      if (!InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP,
+                         false /* optimized */)) {
+        HANDLE_EXCEPTION;
+      }
     }
 
     DISPATCH();
@@ -2026,8 +2041,10 @@
       RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
       InterpreterHelpers::IncrementUsageCounter(
           RAW_CAST(Function, icdata->ptr()->owner_));
-      InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP,
-                    false /* optimized */);
+      if (!InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP,
+                         false /* optimized */)) {
+        HANDLE_EXCEPTION;
+      }
     }
 
     DISPATCH();
@@ -2045,8 +2062,10 @@
 
       RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
       InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
-      InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP,
-                    true /* optimized */);
+      if (!InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP,
+                         true /* optimized */)) {
+        HANDLE_EXCEPTION;
+      }
     }
 
     DISPATCH();
@@ -2064,8 +2083,10 @@
 
       RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
       InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
-      InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP,
-                    true /* optimized */);
+      if (!InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP,
+                         true /* optimized */)) {
+        HANDLE_EXCEPTION;
+      }
     }
 
     DISPATCH();
@@ -2990,7 +3011,7 @@
   }
 
   {
-    BYTECODE(StoreFieldTOS, A_D);
+    BYTECODE(StoreFieldTOS, __D);
     const uword offset_in_words =
         static_cast<uword>(Smi::Value(RAW_CAST(Smi, LOAD_CONSTANT(rD))));
     RawInstance* instance = reinterpret_cast<RawInstance*>(SP[-1]);
diff --git a/runtime/vm/interpreter.h b/runtime/vm/interpreter.h
index 517cbaa..0a87cc4 100644
--- a/runtime/vm/interpreter.h
+++ b/runtime/vm/interpreter.h
@@ -132,7 +132,7 @@
                    RawObject** result,
                    uword target);
 
-  void Invoke(Thread* thread,
+  bool Invoke(Thread* thread,
               RawObject** call_base,
               RawObject** call_top,
               uint32_t** pc,
@@ -163,7 +163,7 @@
                        RawObject** FP,
                        RawObject** SP);
 
-  void InstanceCall1(Thread* thread,
+  bool InstanceCall1(Thread* thread,
                      RawICData* icdata,
                      RawObject** call_base,
                      RawObject** call_top,
@@ -172,7 +172,7 @@
                      RawObject*** SP,
                      bool optimized);
 
-  void InstanceCall2(Thread* thread,
+  bool InstanceCall2(Thread* thread,
                      RawICData* icdata,
                      RawObject** call_base,
                      RawObject** call_top,
diff --git a/runtime/vm/simulator_dbc.cc b/runtime/vm/simulator_dbc.cc
index 7e4ca4e..21d5e11 100644
--- a/runtime/vm/simulator_dbc.cc
+++ b/runtime/vm/simulator_dbc.cc
@@ -2667,7 +2667,7 @@
   }
 
   {
-    BYTECODE(StoreFieldTOS, A_D);
+    BYTECODE(StoreFieldTOS, __D);
     const uint16_t offset_in_words = rD;
     RawInstance* instance = reinterpret_cast<RawInstance*>(SP[-1]);
     RawObject* value = reinterpret_cast<RawObject*>(SP[0]);
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index 9cec45e..63e6fb4 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -325,19 +325,44 @@
   PcDescriptors& descriptors = reused_pc_descriptors_handle.Handle();
   descriptors = code.pc_descriptors();
   PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kAnyKind);
-  while (iter.MoveNext()) {
-    const intptr_t current_try_index = iter.TryIndex();
-    if ((iter.PcOffset() == pc_offset) && (current_try_index != -1)) {
-      ExceptionHandlerInfo handler_info;
-      handlers.GetHandlerInfo(current_try_index, &handler_info);
-      *handler_pc = code.PayloadStart() + handler_info.handler_pc_offset;
-      *needs_stacktrace = handler_info.needs_stacktrace;
-      *has_catch_all = handler_info.has_catch_all;
-      cache->Insert(pc(), handler_info);
-      return true;
+  intptr_t try_index = -1;
+  if (is_interpreted()) {
+    while (iter.MoveNext()) {
+      // PC descriptors for try blocks in bytecode are generated in pairs,
+      // marking start and end of a try block.
+      // See BytecodeMetadataHelper::ReadExceptionsTable for details.
+      const intptr_t current_try_index = iter.TryIndex();
+      const uword start_pc = iter.PcOffset();
+      if (pc_offset < start_pc) {
+        break;
+      }
+      const bool has_next = iter.MoveNext();
+      ASSERT(has_next);
+      const uword end_pc = iter.PcOffset();
+      if (start_pc <= pc_offset && pc_offset < end_pc) {
+        ASSERT(try_index < current_try_index);
+        try_index = current_try_index;
+      }
+    }
+  } else {
+    while (iter.MoveNext()) {
+      const intptr_t current_try_index = iter.TryIndex();
+      if ((iter.PcOffset() == pc_offset) && (current_try_index != -1)) {
+        try_index = current_try_index;
+        break;
+      }
     }
   }
-  return false;
+  if (try_index == -1) {
+    return false;
+  }
+  ExceptionHandlerInfo handler_info;
+  handlers.GetHandlerInfo(try_index, &handler_info);
+  *handler_pc = code.PayloadStart() + handler_info.handler_pc_offset;
+  *needs_stacktrace = handler_info.needs_stacktrace;
+  *has_catch_all = handler_info.has_catch_all;
+  cache->Insert(pc(), handler_info);
+  return true;
 }
 
 TokenPosition StackFrame::GetTokenPos() const {