Version 1.1.1 (stable channel)

svn merge -c 31812 https://dart.googlecode.com/svn/branches/bleeding_edge 1.1

R=kasperl@google.com

Review URL: https://codereview.chromium.org//138543003

git-svn-id: http://dart.googlecode.com/svn/branches/1.1@31822 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/vm/assembler_x64.cc b/runtime/vm/assembler_x64.cc
index 980eb89..2adad1e 100644
--- a/runtime/vm/assembler_x64.cc
+++ b/runtime/vm/assembler_x64.cc
@@ -104,6 +104,32 @@
       object_pool_.Add(Object::null_object(), Heap::kOld);
       patchable_pool_entries_.Add(kNotPatchable);
     }
+
+    // Create fixed object pool entries for debugger stubs.
+    if (StubCode::BreakpointStatic_entry() != NULL) {
+      intptr_t index =
+          FindExternalLabel(&StubCode::BreakpointStaticLabel(), kNotPatchable);
+      ASSERT(index == kBreakpointStaticCPIndex);
+    } else {
+      object_pool_.Add(Object::null_object(), Heap::kOld);
+      patchable_pool_entries_.Add(kNotPatchable);
+    }
+    if (StubCode::BreakpointDynamic_entry() != NULL) {
+      intptr_t index =
+          FindExternalLabel(&StubCode::BreakpointDynamicLabel(), kNotPatchable);
+      ASSERT(index == kBreakpointDynamicCPIndex);
+    } else {
+      object_pool_.Add(Object::null_object(), Heap::kOld);
+      patchable_pool_entries_.Add(kNotPatchable);
+    }
+    if (StubCode::BreakpointRuntime_entry() != NULL) {
+      intptr_t index =
+          FindExternalLabel(&StubCode::BreakpointRuntimeLabel(), kNotPatchable);
+      ASSERT(index == kBreakpointRuntimeCPIndex);
+    } else {
+      object_pool_.Add(Object::null_object(), Heap::kOld);
+      patchable_pool_entries_.Add(kNotPatchable);
+    }
   }
 }
 
diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h
index eca8d26..d59548a 100644
--- a/runtime/vm/assembler_x64.h
+++ b/runtime/vm/assembler_x64.h
@@ -767,6 +767,11 @@
     buffer_.FinalizeInstructions(region);
   }
 
+  // Index of constant pool entries pointing to debugger stubs.
+  static const int kBreakpointStaticCPIndex = 5;
+  static const int kBreakpointDynamicCPIndex = 6;
+  static const int kBreakpointRuntimeCPIndex = 7;
+
   void LoadPoolPointer(Register pp);
 
   // Set up a Dart frame on entry with a frame pointer and PC information to
diff --git a/runtime/vm/code_patcher.h b/runtime/vm/code_patcher.h
index 4fcc1a4..0a7410f 100644
--- a/runtime/vm/code_patcher.h
+++ b/runtime/vm/code_patcher.h
@@ -76,6 +76,9 @@
   static void InsertCallAt(uword start, uword target);
 
   static RawObject* GetEdgeCounterAt(uword pc, const Code& code);
+
+  static int32_t GetPoolOffsetAt(uword return_address);
+  static void SetPoolOffsetAt(uword return_address, int32_t offset);
 };
 
 }  // namespace dart
diff --git a/runtime/vm/code_patcher_arm.cc b/runtime/vm/code_patcher_arm.cc
index 759bf54..6a769cd 100644
--- a/runtime/vm/code_patcher_arm.cc
+++ b/runtime/vm/code_patcher_arm.cc
@@ -46,6 +46,17 @@
 }
 
 
+int32_t CodePatcher::GetPoolOffsetAt(uword return_address) {
+  UNIMPLEMENTED();
+  return 0;
+}
+
+
+void CodePatcher::SetPoolOffsetAt(uword return_address, int32_t offset) {
+  UNIMPLEMENTED();
+}
+
+
 void CodePatcher::InsertCallAt(uword start, uword target) {
   // The inserted call should not overlap the lazy deopt jump code.
   ASSERT(start + CallPattern::kFixedLengthInBytes <= target);
diff --git a/runtime/vm/code_patcher_ia32.cc b/runtime/vm/code_patcher_ia32.cc
index 8143d69..bc7ae62 100644
--- a/runtime/vm/code_patcher_ia32.cc
+++ b/runtime/vm/code_patcher_ia32.cc
@@ -220,6 +220,16 @@
 }
 
 
+int32_t CodePatcher::GetPoolOffsetAt(uword return_address) {
+  UNREACHABLE();
+  return 0;
+}
+
+
+void CodePatcher::SetPoolOffsetAt(uword return_address, int32_t offset) {
+  UNREACHABLE();
+}
+
 
 void CodePatcher::InsertCallAt(uword start, uword target) {
   // The inserted call should not overlap the lazy deopt jump code.
diff --git a/runtime/vm/code_patcher_mips.cc b/runtime/vm/code_patcher_mips.cc
index 7a84596..13195e8 100644
--- a/runtime/vm/code_patcher_mips.cc
+++ b/runtime/vm/code_patcher_mips.cc
@@ -46,6 +46,17 @@
 }
 
 
+int32_t CodePatcher::GetPoolOffsetAt(uword return_address) {
+  UNIMPLEMENTED();
+  return 0;
+}
+
+
+void CodePatcher::SetPoolOffsetAt(uword return_address, int32_t offset) {
+  UNIMPLEMENTED();
+}
+
+
 void CodePatcher::InsertCallAt(uword start, uword target) {
   // The inserted call should not overlap the lazy deopt jump code.
   ASSERT(start + CallPattern::kFixedLengthInBytes <= target);
diff --git a/runtime/vm/code_patcher_x64.cc b/runtime/vm/code_patcher_x64.cc
index 9926411..7311862 100644
--- a/runtime/vm/code_patcher_x64.cc
+++ b/runtime/vm/code_patcher_x64.cc
@@ -142,6 +142,43 @@
 };
 
 
+// The expected pattern of a call where the target is loaded from
+// the object pool:
+//  00: 4d 8b 9f imm32  mov R11, [PP + off]
+//  07: 41 ff d3        call R11
+//  10 <- return address
+class PoolPointerCall : public ValueObject {
+ public:
+  explicit PoolPointerCall(uword return_address)
+      : start_(return_address - kCallPatternSize) {
+    ASSERT(IsValid(return_address));
+  }
+
+  static bool IsValid(uword return_address) {
+    uint8_t* code_bytes =
+        reinterpret_cast<uint8_t*>(return_address - kCallPatternSize);
+    return (code_bytes[0] == 0x4D) && (code_bytes[1] == 0x8B) &&
+           (code_bytes[2] == 0x9F) &&
+           (code_bytes[7] == 0x41) && (code_bytes[8] == 0xFF) &&
+           (code_bytes[9] == 0xD3);
+  }
+
+  int32_t pp_offset() const {
+    return *reinterpret_cast<int32_t*>(start_ + 3);
+  }
+
+  void set_pp_offset(int32_t offset) const {
+    *reinterpret_cast<int32_t*>(start_ + 3) = offset;
+    CPU::FlushICache(start_, kCallPatternSize);
+  }
+
+ private:
+  static const int kCallPatternSize = 7 + 3;
+  uword start_;
+  DISALLOW_IMPLICIT_CONSTRUCTORS(PoolPointerCall);
+};
+
+
 // The expected code pattern of a Dart closure call:
 //  00: 49 ba imm64     mov R10, immediate 2      ; 10 bytes
 //  10: 4d 8b 9f imm32  mov R11, [PP + off]
@@ -200,6 +237,18 @@
 }
 
 
+int32_t CodePatcher::GetPoolOffsetAt(uword return_address) {
+  PoolPointerCall call(return_address);
+  return call.pp_offset();
+}
+
+
+void CodePatcher::SetPoolOffsetAt(uword return_address, int32_t offset) {
+  PoolPointerCall call(return_address);
+  call.set_pp_offset(offset);
+}
+
+
 void CodePatcher::PatchInstanceCallAt(uword return_address,
                                       const Code& code,
                                       uword new_target) {
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index b765acd..20cb409 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -708,6 +708,7 @@
       is_enabled_(false),
       src_bpt_(NULL),
       next_(NULL) {
+  saved_value_ = 0;
   ASSERT(!func.HasOptimizedCode());
   Code& code = Code::Handle(func.unoptimized_code());
   ASSERT(!code.IsNull());  // Function must be compiled.
@@ -758,72 +759,6 @@
 }
 
 
-void CodeBreakpoint::PatchCode() {
-  ASSERT(!is_enabled_);
-  switch (breakpoint_kind_) {
-    case PcDescriptors::kIcCall: {
-      const Code& code =
-          Code::Handle(Function::Handle(function_).unoptimized_code());
-      saved_bytes_.target_address_ =
-          CodePatcher::GetInstanceCallAt(pc_, code, NULL);
-      CodePatcher::PatchInstanceCallAt(pc_, code,
-                                       StubCode::BreakpointDynamicEntryPoint());
-      break;
-    }
-    case PcDescriptors::kUnoptStaticCall: {
-      const Code& code =
-          Code::Handle(Function::Handle(function_).unoptimized_code());
-      saved_bytes_.target_address_ =
-          CodePatcher::GetStaticCallTargetAt(pc_, code);
-      CodePatcher::PatchStaticCallAt(pc_, code,
-                                     StubCode::BreakpointStaticEntryPoint());
-      break;
-    }
-    case PcDescriptors::kRuntimeCall:
-    case PcDescriptors::kClosureCall:
-    case PcDescriptors::kReturn: {
-      const Code& code =
-          Code::Handle(Function::Handle(function_).unoptimized_code());
-      saved_bytes_.target_address_ =
-          CodePatcher::GetStaticCallTargetAt(pc_, code);
-      CodePatcher::PatchStaticCallAt(pc_, code,
-                                     StubCode::BreakpointRuntimeEntryPoint());
-      break;
-    }
-    default:
-      UNREACHABLE();
-  }
-  is_enabled_ = true;
-}
-
-
-void CodeBreakpoint::RestoreCode() {
-  ASSERT(is_enabled_);
-  switch (breakpoint_kind_) {
-    case PcDescriptors::kIcCall: {
-      const Code& code =
-          Code::Handle(Function::Handle(function_).unoptimized_code());
-      CodePatcher::PatchInstanceCallAt(pc_, code,
-                                       saved_bytes_.target_address_);
-      break;
-    }
-    case PcDescriptors::kUnoptStaticCall:
-    case PcDescriptors::kClosureCall:
-    case PcDescriptors::kRuntimeCall:
-    case PcDescriptors::kReturn: {
-      const Code& code =
-          Code::Handle(Function::Handle(function_).unoptimized_code());
-      CodePatcher::PatchStaticCallAt(pc_, code,
-                                     saved_bytes_.target_address_);
-      break;
-    }
-    default:
-      UNREACHABLE();
-  }
-  is_enabled_ = false;
-}
-
-
 void CodeBreakpoint::Enable() {
   if (!is_enabled_) {
     PatchCode();
@@ -1029,14 +964,14 @@
   ASSERT(!code.IsNull());
   PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
   for (intptr_t i = 0; i < desc.Length(); i++) {
-    CodeBreakpoint* bpt = GetCodeBreakpoint(desc.PC(i));
-    if (bpt != NULL) {
-      // There is already a breakpoint for this address. Make sure
-      // it is enabled.
-      bpt->Enable();
-      continue;
-    }
     if (IsSafePoint(desc.DescriptorKind(i))) {
+      CodeBreakpoint* bpt = GetCodeBreakpoint(desc.PC(i));
+      if (bpt != NULL) {
+        // There is already a breakpoint for this address. Make sure
+        // it is enabled.
+        bpt->Enable();
+        continue;
+      }
       bpt = new CodeBreakpoint(target_function, i);
       RegisterCodeBreakpoint(bpt);
       bpt->Enable();
@@ -2175,7 +2110,7 @@
 uword Debugger::GetPatchedStubAddress(uword breakpoint_address) {
   CodeBreakpoint* bpt = GetCodeBreakpoint(breakpoint_address);
   if (bpt != NULL) {
-    return bpt->saved_bytes_.target_address_;
+    return bpt->OrigStubAddress();
   }
   UNREACHABLE();
   return 0L;
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index f7024d3..573ea50 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -90,6 +90,8 @@
   void Disable();
   bool IsEnabled() const { return is_enabled_; }
 
+  uword OrigStubAddress() const;
+
  private:
   void VisitObjectPointers(ObjectPointerVisitor* visitor);
 
@@ -114,10 +116,7 @@
   CodeBreakpoint* next_;
 
   PcDescriptors::Kind breakpoint_kind_;
-  union {
-    uword target_address_;
-    uint8_t raw[2 * sizeof(uword)];
-  } saved_bytes_;
+  uword saved_value_;
 
   friend class Debugger;
   DISALLOW_COPY_AND_ASSIGN(CodeBreakpoint);
diff --git a/runtime/vm/debugger_arm.cc b/runtime/vm/debugger_arm.cc
index b93f32a..753f781 100644
--- a/runtime/vm/debugger_arm.cc
+++ b/runtime/vm/debugger_arm.cc
@@ -5,6 +5,7 @@
 #include "vm/globals.h"
 #if defined(TARGET_ARCH_ARM)
 
+#include "vm/code_patcher.h"
 #include "vm/cpu.h"
 #include "vm/debugger.h"
 #include "vm/instructions.h"
@@ -31,6 +32,72 @@
              *reinterpret_cast<uword*>(closure_addr));
 }
 
+
+uword CodeBreakpoint::OrigStubAddress() const {
+  return saved_value_;
+}
+
+
+void CodeBreakpoint::PatchCode() {
+  ASSERT(!is_enabled_);
+  switch (breakpoint_kind_) {
+    case PcDescriptors::kIcCall: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      saved_value_ = CodePatcher::GetInstanceCallAt(pc_, code, NULL);
+      CodePatcher::PatchInstanceCallAt(pc_, code,
+                                       StubCode::BreakpointDynamicEntryPoint());
+      break;
+    }
+    case PcDescriptors::kUnoptStaticCall: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
+      CodePatcher::PatchStaticCallAt(pc_, code,
+                                     StubCode::BreakpointStaticEntryPoint());
+      break;
+    }
+    case PcDescriptors::kRuntimeCall:
+    case PcDescriptors::kClosureCall:
+    case PcDescriptors::kReturn: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
+      CodePatcher::PatchStaticCallAt(pc_, code,
+                                     StubCode::BreakpointRuntimeEntryPoint());
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+  is_enabled_ = true;
+}
+
+
+void CodeBreakpoint::RestoreCode() {
+  ASSERT(is_enabled_);
+  switch (breakpoint_kind_) {
+    case PcDescriptors::kIcCall: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      CodePatcher::PatchInstanceCallAt(pc_, code, saved_value_);
+      break;
+    }
+    case PcDescriptors::kUnoptStaticCall:
+    case PcDescriptors::kClosureCall:
+    case PcDescriptors::kRuntimeCall:
+    case PcDescriptors::kReturn: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      CodePatcher::PatchStaticCallAt(pc_, code, saved_value_);
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+  is_enabled_ = false;
+}
+
 }  // namespace dart
 
 #endif  // defined TARGET_ARCH_ARM
diff --git a/runtime/vm/debugger_ia32.cc b/runtime/vm/debugger_ia32.cc
index 1e73de4..810e6a1 100644
--- a/runtime/vm/debugger_ia32.cc
+++ b/runtime/vm/debugger_ia32.cc
@@ -36,6 +36,72 @@
       *reinterpret_cast<uword*>(closure_addr));
 }
 
+
+uword CodeBreakpoint::OrigStubAddress() const {
+  return saved_value_;
+}
+
+
+void CodeBreakpoint::PatchCode() {
+  ASSERT(!is_enabled_);
+  switch (breakpoint_kind_) {
+    case PcDescriptors::kIcCall: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      saved_value_ = CodePatcher::GetInstanceCallAt(pc_, code, NULL);
+      CodePatcher::PatchInstanceCallAt(pc_, code,
+                                       StubCode::BreakpointDynamicEntryPoint());
+      break;
+    }
+    case PcDescriptors::kUnoptStaticCall: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
+      CodePatcher::PatchStaticCallAt(pc_, code,
+                                     StubCode::BreakpointStaticEntryPoint());
+      break;
+    }
+    case PcDescriptors::kRuntimeCall:
+    case PcDescriptors::kClosureCall:
+    case PcDescriptors::kReturn: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
+      CodePatcher::PatchStaticCallAt(pc_, code,
+                                     StubCode::BreakpointRuntimeEntryPoint());
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+  is_enabled_ = true;
+}
+
+
+void CodeBreakpoint::RestoreCode() {
+  ASSERT(is_enabled_);
+  switch (breakpoint_kind_) {
+    case PcDescriptors::kIcCall: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      CodePatcher::PatchInstanceCallAt(pc_, code, saved_value_);
+      break;
+    }
+    case PcDescriptors::kUnoptStaticCall:
+    case PcDescriptors::kClosureCall:
+    case PcDescriptors::kRuntimeCall:
+    case PcDescriptors::kReturn: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      CodePatcher::PatchStaticCallAt(pc_, code, saved_value_);
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+  is_enabled_ = false;
+}
+
 }  // namespace dart
 
 #endif  // defined TARGET_ARCH_IA32
diff --git a/runtime/vm/debugger_mips.cc b/runtime/vm/debugger_mips.cc
index 992a860..9a170b9 100644
--- a/runtime/vm/debugger_mips.cc
+++ b/runtime/vm/debugger_mips.cc
@@ -5,6 +5,7 @@
 #include "vm/globals.h"
 #if defined(TARGET_ARCH_MIPS)
 
+#include "vm/code_patcher.h"
 #include "vm/cpu.h"
 #include "vm/debugger.h"
 #include "vm/instructions.h"
@@ -31,6 +32,72 @@
              *reinterpret_cast<uword*>(closure_addr));
 }
 
+
+uword CodeBreakpoint::OrigStubAddress() const {
+  return saved_value_;
+}
+
+
+void CodeBreakpoint::PatchCode() {
+  ASSERT(!is_enabled_);
+  switch (breakpoint_kind_) {
+    case PcDescriptors::kIcCall: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      saved_value_ = CodePatcher::GetInstanceCallAt(pc_, code, NULL);
+      CodePatcher::PatchInstanceCallAt(pc_, code,
+                                       StubCode::BreakpointDynamicEntryPoint());
+      break;
+    }
+    case PcDescriptors::kUnoptStaticCall: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
+      CodePatcher::PatchStaticCallAt(pc_, code,
+                                     StubCode::BreakpointStaticEntryPoint());
+      break;
+    }
+    case PcDescriptors::kRuntimeCall:
+    case PcDescriptors::kClosureCall:
+    case PcDescriptors::kReturn: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
+      CodePatcher::PatchStaticCallAt(pc_, code,
+                                     StubCode::BreakpointRuntimeEntryPoint());
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+  is_enabled_ = true;
+}
+
+
+void CodeBreakpoint::RestoreCode() {
+  ASSERT(is_enabled_);
+  switch (breakpoint_kind_) {
+    case PcDescriptors::kIcCall: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      CodePatcher::PatchInstanceCallAt(pc_, code, saved_value_);
+      break;
+    }
+    case PcDescriptors::kUnoptStaticCall:
+    case PcDescriptors::kClosureCall:
+    case PcDescriptors::kRuntimeCall:
+    case PcDescriptors::kReturn: {
+      const Code& code =
+          Code::Handle(Function::Handle(function_).unoptimized_code());
+      CodePatcher::PatchStaticCallAt(pc_, code, saved_value_);
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+  is_enabled_ = false;
+}
+
 }  // namespace dart
 
 #endif  // defined TARGET_ARCH_MIPS
diff --git a/runtime/vm/debugger_x64.cc b/runtime/vm/debugger_x64.cc
index 8dacf61..653e3ec 100644
--- a/runtime/vm/debugger_x64.cc
+++ b/runtime/vm/debugger_x64.cc
@@ -8,7 +8,9 @@
 #include "vm/debugger.h"
 
 #include "vm/assembler.h"
+#include "vm/code_patcher.h"
 #include "vm/cpu.h"
+#include "vm/instructions.h"
 #include "vm/stub_code.h"
 
 namespace dart {
@@ -32,6 +34,80 @@
              *reinterpret_cast<uword*>(closure_addr));
 }
 
+
+uword CodeBreakpoint::OrigStubAddress() const {
+  const Code& code =
+      Code::Handle(Function::Handle(function_).unoptimized_code());
+  const Array& object_pool = Array::Handle(code.ObjectPool());
+  uword offset = saved_value_ + kHeapObjectTag;
+  ASSERT((offset % kWordSize) == 0);
+  const intptr_t index = (offset - Array::data_offset()) / kWordSize;
+  const uword stub_address = reinterpret_cast<uword>(object_pool.At(index));
+  ASSERT(stub_address % kWordSize == 0);
+  return stub_address;
+}
+
+
+void CodeBreakpoint::PatchCode() {
+  ASSERT(!is_enabled_);
+  switch (breakpoint_kind_) {
+    case PcDescriptors::kIcCall: {
+      int32_t offset = CodePatcher::GetPoolOffsetAt(pc_);
+      ASSERT((offset > 0) && ((offset % 8) == 7));
+      saved_value_ = static_cast<uword>(offset);
+      const int32_t stub_offset =
+          InstructionPattern::OffsetFromPPIndex(
+              Assembler::kBreakpointDynamicCPIndex);
+      CodePatcher::SetPoolOffsetAt(pc_, stub_offset);
+      break;
+    }
+    case PcDescriptors::kUnoptStaticCall: {
+      int32_t offset = CodePatcher::GetPoolOffsetAt(pc_);
+      ASSERT((offset > 0) && ((offset % 8) == 7));
+      saved_value_ = static_cast<uword>(offset);
+      const uint32_t stub_offset =
+          InstructionPattern::OffsetFromPPIndex(
+              Assembler::kBreakpointStaticCPIndex);
+      CodePatcher::SetPoolOffsetAt(pc_, stub_offset);
+      break;
+    }
+    case PcDescriptors::kRuntimeCall:
+    case PcDescriptors::kClosureCall:
+    case PcDescriptors::kReturn: {
+      int32_t offset = CodePatcher::GetPoolOffsetAt(pc_);
+      ASSERT((offset > 0) && ((offset % 8) == 7));
+      saved_value_ = static_cast<uword>(offset);
+      const uint32_t stub_offset =
+          InstructionPattern::OffsetFromPPIndex(
+              Assembler::kBreakpointRuntimeCPIndex);
+      CodePatcher::SetPoolOffsetAt(pc_, stub_offset);
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+  is_enabled_ = true;
+}
+
+
+void CodeBreakpoint::RestoreCode() {
+  ASSERT(is_enabled_);
+  switch (breakpoint_kind_) {
+    case PcDescriptors::kIcCall:
+    case PcDescriptors::kUnoptStaticCall:
+    case PcDescriptors::kClosureCall:
+    case PcDescriptors::kRuntimeCall:
+    case PcDescriptors::kReturn: {
+      CodePatcher::SetPoolOffsetAt(pc_, static_cast<int32_t>(saved_value_));
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+  is_enabled_ = false;
+}
+
+
 }  // namespace dart
 
 #endif  // defined TARGET_ARCH_X64
diff --git a/runtime/vm/instructions_x64.cc b/runtime/vm/instructions_x64.cc
index 7b0b4ae..f01afbb 100644
--- a/runtime/vm/instructions_x64.cc
+++ b/runtime/vm/instructions_x64.cc
@@ -18,6 +18,12 @@
 }
 
 
+intptr_t InstructionPattern::OffsetFromPPIndex(intptr_t index) {
+  intptr_t offset = Array::element_offset(index);
+  return offset - kHeapObjectTag;
+}
+
+
 bool InstructionPattern::TestBytesWith(const int* data, int num_bytes) const {
   ASSERT(data != NULL);
   const uint8_t* byte_array = reinterpret_cast<const uint8_t*>(start_);
diff --git a/runtime/vm/instructions_x64.h b/runtime/vm/instructions_x64.h
index 8b91e7d..ced627c 100644
--- a/runtime/vm/instructions_x64.h
+++ b/runtime/vm/instructions_x64.h
@@ -39,6 +39,7 @@
   virtual int pattern_length_in_bytes() const = 0;
 
   static intptr_t IndexFromPPLoad(uword start);
+  static intptr_t OffsetFromPPIndex(intptr_t index);
 
  protected:
   uword start() const { return start_; }
diff --git a/tools/VERSION b/tools/VERSION
index 365f423..2d6a7b4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -26,6 +26,6 @@
 CHANNEL stable
 MAJOR 1
 MINOR 1
-PATCH 0
+PATCH 1
 PRERELEASE 0
 PRERELEASE_PATCH 0