[vm] Greatly reduce handle allocation during reload.

Saves 10ms from non-empty reloads of Flutter Gallery.

Change-Id: If9a1f9970d562f8fd7996de875dfe231350efaa9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/113282
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc
index a3f6a6e..27cdef7 100644
--- a/runtime/vm/compiler/backend/constant_propagator.cc
+++ b/runtime/vm/compiler/backend/constant_propagator.cc
@@ -608,7 +608,7 @@
           instr->kind(),
           Integer::Handle(Z, Integer::Cast(left).BitOp(Token::kBIT_AND,
                                                        Integer::Cast(right))),
-          Smi::Handle(Z, Smi::New(0)));
+          Object::smi_zero());
       SetValue(instr, result ? Bool::True() : Bool::False());
     } else {
       SetValue(instr, non_constant_);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 2f3c3af..a0db6a8 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -223,9 +223,8 @@
     const intptr_t num_counters = flow_graph_.preorder().length();
     const Array& edge_counters =
         Array::Handle(Array::New(num_counters, Heap::kOld));
-    const Smi& zero_smi = Smi::Handle(Smi::New(0));
     for (intptr_t i = 0; i < num_counters; ++i) {
-      edge_counters.SetAt(i, zero_smi);
+      edge_counters.SetAt(i, Object::smi_zero());
     }
     edge_counters_array_ = edge_counters.raw();
   }
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index e39e0fa..8cbf68a 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -2623,7 +2623,7 @@
             new DeoptimizeInstr(ICData::kDeoptBinarySmiOp, GetDeoptId());
         flow_graph->InsertBefore(this, deopt, env(), FlowGraph::kEffect);
         // Replace with zero since it always throws.
-        return CreateConstantResult(flow_graph, Integer::Handle(Smi::New(0)));
+        return CreateConstantResult(flow_graph, Object::smi_zero());
       }
       break;
 
@@ -2633,7 +2633,7 @@
         return left()->definition();
       } else if ((rhs >= kBitsPerInt64) ||
                  ((rhs >= result_bits) && is_truncating())) {
-        return CreateConstantResult(flow_graph, Integer::Handle(Smi::New(0)));
+        return CreateConstantResult(flow_graph, Object::smi_zero());
       } else if ((rhs < 0) || ((rhs >= result_bits) && !is_truncating())) {
         // Instruction will always throw on negative rhs operand or
         // deoptimize on large rhs operand.
@@ -2647,7 +2647,7 @@
             new DeoptimizeInstr(ICData::kDeoptBinarySmiOp, GetDeoptId());
         flow_graph->InsertBefore(this, deopt, env(), FlowGraph::kEffect);
         // Replace with zero since it overshifted or always throws.
-        return CreateConstantResult(flow_graph, Integer::Handle(Smi::New(0)));
+        return CreateConstantResult(flow_graph, Object::smi_zero());
       }
       break;
     }
@@ -2898,7 +2898,7 @@
                  MethodRecognizer::kByteDataFactory) {
         // A _ByteDataView returned from the ByteData constructor always
         // has an offset of 0.
-        return flow_graph->GetConstant(Smi::Handle(Smi::New(0)));
+        return flow_graph->GetConstant(Object::smi_zero());
       }
     }
   } else if (slot().IsTypeArguments()) {
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index a71a380..8f5492a 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -2891,7 +2891,7 @@
   // Check adjusted_length > 0.
   // TODO(ajcbik): this is a synthetic check that cannot
   // be directly linked to a use, is that a sign of wrong use?
-  ConstantInstr* zero = flow_graph->GetConstant(Smi::Handle(Z, Smi::New(0)));
+  ConstantInstr* zero = flow_graph->GetConstant(Object::smi_zero());
   Definition* check =
       flow_graph->CreateCheckBound(adjusted_length, zero, call->deopt_id());
   *cursor =
diff --git a/runtime/vm/compiler/backend/range_analysis.cc b/runtime/vm/compiler/backend/range_analysis.cc
index b350e13..84e1557 100644
--- a/runtime/vm/compiler/backend/range_analysis.cc
+++ b/runtime/vm/compiler/backend/range_analysis.cc
@@ -1137,7 +1137,7 @@
         }
 
         if (left == NULL) {
-          left = flow_graph_->GetConstant(Smi::Handle(Smi::New(0)));
+          left = flow_graph_->GetConstant(Object::smi_zero());
         }
 
         if (right == NULL) {
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 14f3026..5d8dc50 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -17,6 +17,7 @@
 #include "vm/flags.h"
 #include "vm/globals.h"
 #include "vm/interpreter.h"
+#include "vm/isolate_reload.h"
 #include "vm/json_stream.h"
 #include "vm/kernel.h"
 #include "vm/longjump.h"
@@ -2032,6 +2033,7 @@
   // TODO(hausner): Could possibly be combined with RemoveOptimizedCode()
   const ClassTable& class_table = *isolate_->class_table();
   Zone* zone = Thread::Current()->zone();
+  CallSiteResetter resetter(zone);
   Class& cls = Class::Handle(zone);
   Array& functions = Array::Handle(zone);
   GrowableObjectArray& closures = GrowableObjectArray::Handle(zone);
@@ -2060,7 +2062,7 @@
           }
           code = function.unoptimized_code();
           if (!code.IsNull()) {
-            code.ResetSwitchableCalls(zone);
+            resetter.ResetSwitchableCalls(code);
           }
           // Also disable any optimized implicit closure functions.
           if (function.HasImplicitClosureFunction()) {
@@ -2070,7 +2072,7 @@
             }
             code = function.unoptimized_code();
             if (!code.IsNull()) {
-              code.ResetSwitchableCalls(zone);
+              resetter.ResetSwitchableCalls(code);
             }
           }
         }
@@ -2089,7 +2091,7 @@
     }
     code = function.unoptimized_code();
     if (!code.IsNull()) {
-      code.ResetSwitchableCalls(zone);
+      resetter.ResetSwitchableCalls(code);
     }
   }
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 5fe3c39..87cfc24 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -1883,13 +1883,14 @@
   Code& code = Code::Handle(zone);
   Bytecode& bytecode = Bytecode::Handle(zone);
   Function& function = Function::Handle(zone);
+  CallSiteResetter resetter(zone);
   DartFrameIterator iterator(thread,
                              StackFrameIterator::kNoCrossThreadIteration);
   StackFrame* frame = iterator.NextFrame();
   while (frame != NULL) {
     if (frame->is_interpreted()) {
       bytecode = frame->LookupDartBytecode();
-      bytecode.ResetICDatas(zone);
+      resetter.ResetICDatas(bytecode);
     } else {
       code = frame->LookupDartCode();
       if (code.is_optimized() && !code.is_force_optimized()) {
@@ -1899,11 +1900,11 @@
         function = code.function();
         code = function.unoptimized_code();
         ASSERT(!code.IsNull());
-        code.ResetSwitchableCalls(zone);
-        code.ResetICDatas(zone);
+        resetter.ResetSwitchableCalls(code);
+        resetter.ResetICDatas(code);
       } else {
-        code.ResetSwitchableCalls(zone);
-        code.ResetICDatas(zone);
+        resetter.ResetSwitchableCalls(code);
+        resetter.ResetICDatas(code);
       }
     }
     frame = iterator.NextFrame();
@@ -1985,6 +1986,8 @@
     }
   }
 
+  CallSiteResetter resetter(zone);
+
   Class& owning_class = Class::Handle(zone);
   Library& owning_lib = Library::Handle(zone);
   Code& code = Code::Handle(zone);
@@ -2010,12 +2013,12 @@
 
     // Zero edge counters, before clearing the ICDataArray, since that's where
     // they're held.
-    func.ZeroEdgeCounters();
+    resetter.ZeroEdgeCounters(func);
 
     if (!bytecode.IsNull()) {
       // We are preserving the bytecode, fill all ICData arrays with
       // the sentinel values so that we have no stale type feedback.
-      bytecode.ResetICDatas(zone);
+      resetter.ResetICDatas(bytecode);
     }
 
     if (stub_code) {
@@ -2030,8 +2033,8 @@
     } else {
       // We are preserving the unoptimized code, fill all ICData arrays with
       // the sentinel values so that we have no stale type feedback.
-      code.ResetSwitchableCalls(zone);
-      code.ResetICDatas(zone);
+      resetter.ResetSwitchableCalls(code);
+      resetter.ResetICDatas(code);
     }
 
     // Clear counters.
diff --git a/runtime/vm/isolate_reload.h b/runtime/vm/isolate_reload.h
index f21c539..6937915 100644
--- a/runtime/vm/isolate_reload.h
+++ b/runtime/vm/isolate_reload.h
@@ -372,6 +372,38 @@
   static Dart_FileModifiedCallback file_modified_callback_;
 };
 
+class CallSiteResetter : public ValueObject {
+ public:
+  explicit CallSiteResetter(Zone* zone);
+
+  void ZeroEdgeCounters(const Function& function);
+  void ResetICDatas(const Code& code);
+  void ResetICDatas(const Bytecode& code);
+  void ResetICDatas(const ObjectPool& pool);
+  void Reset(const ICData& ic);
+  void ResetSwitchableCalls(const Code& code);
+
+ private:
+  Zone* zone_;
+  Instructions& instrs_;
+  ObjectPool& pool_;
+  Object& object_;
+  String& name_;
+  Class& new_cls_;
+  Library& new_lib_;
+  Function& new_function_;
+  Field& new_field_;
+  Array& entries_;
+  Function& old_target_;
+  Function& new_target_;
+  Function& caller_;
+  Array& args_desc_array_;
+  Array& ic_data_array_;
+  Array& edge_counters_;
+  PcDescriptors& descriptors_;
+  ICData& ic_data_;
+};
+
 }  // namespace dart
 
 #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 1a07cc9..b9322cf 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -871,6 +871,7 @@
   *bool_false_ = Bool::New(false);
 
   *smi_illegal_cid_ = Smi::New(kIllegalCid);
+  *smi_zero_ = Smi::New(0);
 
   String& error_str = String::Handle();
   error_str = String::New("SnapshotWriter Error", Heap::kOld);
@@ -962,6 +963,7 @@
   ASSERT(!bool_false_->IsSmi());
   ASSERT(bool_false_->IsBool());
   ASSERT(smi_illegal_cid_->IsSmi());
+  ASSERT(smi_zero_->IsSmi());
   ASSERT(!snapshot_writer_error_->IsSmi());
   ASSERT(snapshot_writer_error_->IsLanguageError());
   ASSERT(!branch_offset_error_->IsSmi());
@@ -10995,7 +10997,7 @@
 static RawArray* NewDictionary(intptr_t initial_size) {
   const Array& dict = Array::Handle(Array::New(initial_size + 1, Heap::kOld));
   // The last element of the dictionary specifies the number of in use slots.
-  dict.SetAt(initial_size, Smi::Handle(Smi::New(0)));
+  dict.SetAt(initial_size, Object::smi_zero());
   return dict.raw();
 }
 
@@ -13685,7 +13687,10 @@
   const intptr_t len = Length();
   ASSERT(index >= 0);
   ASSERT(index < len);
-  Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   const intptr_t start = index * TestEntryLength();
   const intptr_t end = start + TestEntryLength();
   for (intptr_t i = start; i < end; i++) {
@@ -13712,7 +13717,10 @@
   const intptr_t num_args_tested = NumArgsTested();
   if (num_args_tested == 0) {
     // No type feedback is being collected.
-    const Array& data = Array::Handle(entries());
+    Thread* thread = Thread::Current();
+    REUSABLE_ARRAY_HANDLESCOPE(thread);
+    Array& data = thread->ArrayHandle();
+    data = entries();
     // Static calls with no argument checks hold only one target and the
     // sentinel value.
     ASSERT(len == 2);
@@ -13722,24 +13730,24 @@
     data.SetAt(TargetIndexFor(num_args_tested), func);
     // Set count to 0 as this is called during compilation, before the
     // call has been executed.
-    const Smi& value = Smi::Handle(Smi::New(0));
-    data.SetAt(CountIndexFor(num_args_tested), value);
+    data.SetAt(CountIndexFor(num_args_tested), Object::smi_zero());
   } else {
     // Type feedback on arguments is being collected.
-    const Array& data = Array::Handle(entries());
-
     // Fill all but the first entry with the sentinel.
     for (intptr_t i = len - 1; i > 0; i--) {
       WriteSentinelAt(i);
     }
+    Thread* thread = Thread::Current();
+    REUSABLE_ARRAY_HANDLESCOPE(thread);
+    Array& data = thread->ArrayHandle();
+    data = entries();
     // Rewrite the dummy entry.
     const Smi& object_cid = Smi::Handle(Smi::New(kObjectCid));
     for (intptr_t i = 0; i < NumArgsTested(); i++) {
       data.SetAt(i, object_cid);
     }
     data.SetAt(TargetIndexFor(num_args_tested), func);
-    const Smi& value = Smi::Handle(Smi::New(0));
-    data.SetAt(CountIndexFor(num_args_tested), value);
+    data.SetAt(CountIndexFor(num_args_tested), Object::smi_zero());
   }
 }
 
@@ -13803,7 +13811,10 @@
   // Can add only once.
   const intptr_t old_num = NumberOfChecks();
   ASSERT(old_num == 0);
-  Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   const intptr_t new_len = data.Length() + TestEntryLength();
   data = Array::Grow(data, new_len, Heap::kOld);
   WriteSentinel(data, TestEntryLength());
@@ -13812,8 +13823,7 @@
   data.SetAt(data_pos + TargetIndexFor(NumArgsTested()), target);
   // Set count to 0 as this is called during compilation, before the
   // call has been executed.
-  const Smi& value = Smi::Handle(Smi::New(0));
-  data.SetAt(data_pos + CountIndexFor(NumArgsTested()), value);
+  data.SetAt(data_pos + CountIndexFor(NumArgsTested()), Object::smi_zero());
   // Multithreaded access to ICData requires setting of array to be the last
   // operation.
   set_entries(data);
@@ -13977,7 +13987,10 @@
   if (!is_tracking_exactness()) {
     return StaticTypeExactnessState::NotTracking();
   }
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   intptr_t data_pos =
       index * TestEntryLength() + ExactnessIndexFor(NumArgsTested());
   return StaticTypeExactnessState::Decode(
@@ -13991,7 +14004,10 @@
   ASSERT(class_ids != NULL);
   ASSERT(target != NULL);
   class_ids->Clear();
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   intptr_t data_pos = index * TestEntryLength();
   for (intptr_t i = 0; i < NumArgsTested(); i++) {
     class_ids->Add(Smi::Value(Smi::RawCast(data.At(data_pos + i))));
@@ -14001,7 +14017,10 @@
 
 bool ICData::IsSentinelAt(intptr_t index) const {
   ASSERT(index < Length());
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   const intptr_t entry_length = TestEntryLength();
   intptr_t data_pos = index * TestEntryLength();
   for (intptr_t i = 0; i < entry_length; i++) {
@@ -14019,7 +14038,10 @@
   ASSERT(class_ids != NULL);
   ASSERT(!IsSentinelAt(index));
   class_ids->Clear();
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   intptr_t data_pos = index * TestEntryLength();
   for (intptr_t i = 0; i < NumArgsTested(); i++) {
     class_ids->Add(Smi::Value(Smi::RawCast(data.At(data_pos++))));
@@ -14032,7 +14054,10 @@
   ASSERT(class_id != NULL);
   ASSERT(target != NULL);
   ASSERT(NumArgsTested() == 1);
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   const intptr_t data_pos = index * TestEntryLength();
   *class_id = Smi::Value(Smi::RawCast(data.At(data_pos)));
   *target ^= data.At(data_pos + TargetIndexFor(NumArgsTested()));
@@ -14040,7 +14065,10 @@
 
 intptr_t ICData::GetCidAt(intptr_t index) const {
   ASSERT(NumArgsTested() == 1);
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   const intptr_t data_pos = index * TestEntryLength();
   return Smi::Value(Smi::RawCast(data.At(data_pos)));
 }
@@ -14090,7 +14118,10 @@
   ASSERT(0 <= value);
   ASSERT(value <= Smi::kMaxValue);
 
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   const intptr_t data_pos =
       index * TestEntryLength() + CountIndexFor(NumArgsTested());
   data.SetAt(data_pos, Smi::Handle(Smi::New(value)));
@@ -14098,7 +14129,10 @@
 
 intptr_t ICData::GetCountAt(intptr_t index) const {
   ASSERT(Isolate::Current()->compilation_allowed());
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   const intptr_t data_pos =
       index * TestEntryLength() + CountIndexFor(NumArgsTested());
   intptr_t value = Smi::Value(Smi::RawCast(data.At(data_pos)));
@@ -14122,7 +14156,10 @@
 
 void ICData::SetCodeAt(intptr_t index, const Code& value) const {
   ASSERT(!Isolate::Current()->compilation_allowed());
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   const intptr_t data_pos =
       index * TestEntryLength() + CodeIndexFor(NumArgsTested());
   data.SetAt(data_pos, value);
@@ -14130,7 +14167,10 @@
 
 void ICData::SetEntryPointAt(intptr_t index, const Smi& value) const {
   ASSERT(!Isolate::Current()->compilation_allowed());
-  const Array& data = Array::Handle(entries());
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
   const intptr_t data_pos =
       index * TestEntryLength() + EntryPointIndexFor(NumArgsTested());
   data.SetAt(data_pos, value);
@@ -14432,8 +14472,7 @@
 }
 
 bool ICData::IsImmutable() const {
-  const Array& data = Array::Handle(entries());
-  return data.IsImmutable();
+  return entries()->IsImmutableArray();
 }
 
 RawICData* ICData::New() {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 09a7c02..c5ab60c 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -413,6 +413,7 @@
   V(Bool, bool_true)                                                           \
   V(Bool, bool_false)                                                          \
   V(Smi, smi_illegal_cid)                                                      \
+  V(Smi, smi_zero)                                                             \
   V(LanguageError, snapshot_writer_error)                                      \
   V(LanguageError, branch_offset_error)                                        \
   V(LanguageError, speculative_inlining_error)                                 \
@@ -2065,6 +2066,7 @@
   friend class SnapshotWriter;
   friend class Serializer;
   friend class Deserializer;
+  friend class CallSiteResetter;
 };
 
 // Often used constants for number of free function type parameters.
@@ -2172,10 +2174,6 @@
                                 intptr_t num_free_fun_type_params = kAllFree,
                                 TrailPtr trail = NULL) const;
 
-  // Reloading support:
-  void Reparent(const Class& new_cls) const;
-  void ZeroEdgeCounters() const;
-
   RawClass* Owner() const;
   void set_owner(const Object& value) const;
   RawClass* origin() const;
@@ -4567,9 +4565,6 @@
     StoreNonPointer(&EntryAddr(index)->raw_value_, raw_value);
   }
 
-  // Used during reloading (see object_reload.cc). Calls Reset on all ICDatas.
-  void ResetICDatas(Zone* zone) const;
-
   static intptr_t InstanceSize() {
     ASSERT(sizeof(RawObjectPool) ==
            OFFSET_OF_RETURNED_VALUE(RawObjectPool, data));
@@ -5292,11 +5287,6 @@
     StorePointer(&raw_ptr()->code_source_map_, code_source_map.raw());
   }
 
-  // Used during reloading (see object_reload.cc). Calls Reset on all ICDatas
-  // that are embedded inside the Code or ObjecPool objects.
-  void ResetICDatas(Zone* zone) const;
-  void ResetSwitchableCalls(Zone* zone) const;
-
   // Array of DeoptInfo objects.
   RawArray* deopt_info_array() const {
 #if defined(DART_PRECOMPILED_RUNTIME)
@@ -5691,6 +5681,7 @@
   // So that the RawFunction pointer visitor can determine whether code the
   // function points to is optimized.
   friend class RawFunction;
+  friend class CallSiteResetter;
 };
 
 class Bytecode : public Object {
@@ -5731,10 +5722,6 @@
     StorePointer(&raw_ptr()->function_, function.raw());
   }
 
-  // Used during reloading (see object_reload.cc). Calls Reset on all ICDatas
-  // that are embedded inside the Code or ObjecPool objects.
-  void ResetICDatas(Zone* zone) const;
-
   static intptr_t InstanceSize() {
     return RoundedAllocationSize(sizeof(RawBytecode));
   }
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
index c72175b..0120e58 100644
--- a/runtime/vm/object_reload.cc
+++ b/runtime/vm/object_reload.cc
@@ -21,57 +21,55 @@
 DECLARE_FLAG(bool, trace_reload_verbose);
 DECLARE_FLAG(bool, two_args_smi_icd);
 
-class ObjectReloadUtils : public AllStatic {
-  static void DumpLibraryDictionary(const Library& lib) {
-    DictionaryIterator it(lib);
-    Object& entry = Object::Handle();
-    String& name = String::Handle();
-    TIR_Print("Dumping dictionary for %s\n", lib.ToCString());
-    while (it.HasNext()) {
-      entry = it.GetNext();
-      name = entry.DictionaryName();
-      TIR_Print("%s -> %s\n", name.ToCString(), entry.ToCString());
-    }
-  }
-};
-
-void Function::Reparent(const Class& new_cls) const {
-  set_owner(new_cls);
-}
-
-void Function::ZeroEdgeCounters() const {
-  const Array& saved_ic_data = Array::Handle(ic_data_array());
-  if (saved_ic_data.IsNull()) {
+void CallSiteResetter::ZeroEdgeCounters(const Function& function) {
+  ic_data_array_ = function.ic_data_array();
+  if (ic_data_array_.IsNull()) {
     return;
   }
-  const intptr_t saved_ic_datalength = saved_ic_data.Length();
-  ASSERT(saved_ic_datalength > 0);
-  const Array& edge_counters_array =
-      Array::Handle(Array::RawCast(saved_ic_data.At(0)));
-  if (edge_counters_array.IsNull()) {
+  ASSERT(ic_data_array_.Length() > 0);
+  edge_counters_ ^= ic_data_array_.At(0);
+  if (edge_counters_.IsNull()) {
     return;
   }
   // Fill edge counters array with zeros.
-  const Smi& zero = Smi::Handle(Smi::New(0));
-  for (intptr_t i = 0; i < edge_counters_array.Length(); i++) {
-    edge_counters_array.SetAt(i, zero);
+  for (intptr_t i = 0; i < edge_counters_.Length(); i++) {
+    edge_counters_.SetAt(i, Object::smi_zero());
   }
 }
 
-void Code::ResetICDatas(Zone* zone) const {
-// Iterate over the Code's object pool and reset all ICDatas.
+CallSiteResetter::CallSiteResetter(Zone* zone)
+    : zone_(zone),
+      instrs_(Instructions::Handle(zone)),
+      pool_(ObjectPool::Handle(zone)),
+      object_(Object::Handle(zone)),
+      name_(String::Handle(zone)),
+      new_cls_(Class::Handle(zone)),
+      new_lib_(Library::Handle(zone)),
+      new_function_(Function::Handle(zone)),
+      new_field_(Field::Handle(zone)),
+      entries_(Array::Handle(zone)),
+      old_target_(Function::Handle(zone)),
+      new_target_(Function::Handle(zone)),
+      caller_(Function::Handle(zone)),
+      args_desc_array_(Array::Handle(zone)),
+      ic_data_array_(Array::Handle(zone)),
+      edge_counters_(Array::Handle(zone)),
+      descriptors_(PcDescriptors::Handle(zone)),
+      ic_data_(ICData::Handle(zone)) {}
+
+void CallSiteResetter::ResetICDatas(const Code& code) {
+  // Iterate over the Code's object pool and reset all ICDatas.
 #ifdef TARGET_ARCH_IA32
   // IA32 does not have an object pool, but, we can iterate over all
   // embedded objects by using the variable length data section.
-  if (!is_alive()) {
+  if (!code.is_alive()) {
     return;
   }
-  const Instructions& instrs = Instructions::Handle(zone, instructions());
-  ASSERT(!instrs.IsNull());
-  uword base_address = instrs.PayloadStart();
-  Object& object = Object::Handle(zone);
-  intptr_t offsets_length = pointer_offsets_length();
-  const int32_t* offsets = raw_ptr()->data();
+  instrs_ = code.instructions();
+  ASSERT(!instrs_.IsNull());
+  uword base_address = instrs_.PayloadStart();
+  intptr_t offsets_length = code.pointer_offsets_length();
+  const int32_t* offsets = code.raw_ptr()->data();
   for (intptr_t i = 0; i < offsets_length; i++) {
     int32_t offset = offsets[i];
     RawObject** object_ptr =
@@ -80,15 +78,15 @@
     if (!raw_object->IsHeapObject()) {
       continue;
     }
-    object = raw_object;
-    if (object.IsICData()) {
-      ICData::Cast(object).Reset(zone);
+    object_ = raw_object;
+    if (object_.IsICData()) {
+      Reset(ICData::Cast(object_));
     }
   }
 #else
-  const ObjectPool& pool = ObjectPool::Handle(zone, object_pool());
-  ASSERT(!pool.IsNull());
-  pool.ResetICDatas(zone);
+  pool_ = code.object_pool();
+  ASSERT(!pool_.IsNull());
+  ResetICDatas(pool_);
 #endif
 }
 
@@ -117,17 +115,17 @@
 }
 #endif  // !defined(TARGET_ARCH_DBC)
 
-void Code::ResetSwitchableCalls(Zone* zone) const {
+void CallSiteResetter::ResetSwitchableCalls(const Code& code) {
 #if !defined(TARGET_ARCH_DBC)
-  if (is_optimized()) {
+  if (code.is_optimized()) {
     return;  // No switchable calls in optimized code.
   }
 
-  const Object& owner = Object::Handle(zone, this->owner());
-  if (!owner.IsFunction()) {
+  object_ = code.owner();
+  if (!object_.IsFunction()) {
     return;  // No switchable calls in stub code.
   }
-  const Function& function = Function::Cast(owner);
+  const Function& function = Function::Cast(object_);
 
   if (function.kind() == RawFunction::kIrregexpFunction) {
     // Regex matchers do not support breakpoints or stepping, and they only call
@@ -138,102 +136,92 @@
     return;
   }
 
-  const Array& ic_data_array = Array::Handle(zone, function.ic_data_array());
-  if (ic_data_array.IsNull()) {
+  ic_data_array_ = function.ic_data_array();
+  if (ic_data_array_.IsNull()) {
     // The megamorphic miss stub and some recognized function doesn't populate
     // their ic_data_array. Check this only happens for functions without IC
     // calls.
 #if defined(DEBUG)
-    const PcDescriptors& descriptors =
-        PcDescriptors::Handle(zone, pc_descriptors());
-    PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kIcCall);
+    descriptors_ = code.pc_descriptors();
+    PcDescriptors::Iterator iter(descriptors_, RawPcDescriptors::kIcCall);
     while (iter.MoveNext()) {
-      FATAL1("%s has IC calls but no ic_data_array\n", owner.ToCString());
+      FATAL1("%s has IC calls but no ic_data_array\n", object_.ToCString());
     }
 #endif
     return;
   }
 
-  ICData& ic_data = ICData::Handle(zone);
-  Object& data = Object::Handle(zone);
-  const PcDescriptors& descriptors =
-      PcDescriptors::Handle(zone, pc_descriptors());
-  PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kIcCall);
+  descriptors_ = code.pc_descriptors();
+  PcDescriptors::Iterator iter(descriptors_, RawPcDescriptors::kIcCall);
   while (iter.MoveNext()) {
-    uword pc = PayloadStart() + iter.PcOffset();
-    CodePatcher::GetInstanceCallAt(pc, *this, &data);
+    uword pc = code.PayloadStart() + iter.PcOffset();
+    CodePatcher::GetInstanceCallAt(pc, code, &object_);
     // This check both avoids unnecessary patching to reduce log spam and
     // prevents patching over breakpoint stubs.
-    if (!data.IsICData()) {
-      FindICData(ic_data_array, iter.DeoptId(), &ic_data);
-      ASSERT(ic_data.rebind_rule() == ICData::kInstance);
-      ASSERT(ic_data.NumArgsTested() == 1);
+    if (!object_.IsICData()) {
+      FindICData(ic_data_array_, iter.DeoptId(), &ic_data_);
+      ASSERT(ic_data_.rebind_rule() == ICData::kInstance);
+      ASSERT(ic_data_.NumArgsTested() == 1);
       const Code& stub =
-          ic_data.is_tracking_exactness()
+          ic_data_.is_tracking_exactness()
               ? StubCode::OneArgCheckInlineCacheWithExactnessCheck()
               : StubCode::OneArgCheckInlineCache();
-      CodePatcher::PatchInstanceCallAt(pc, *this, ic_data, stub);
+      CodePatcher::PatchInstanceCallAt(pc, code, ic_data_, stub);
       if (FLAG_trace_ic) {
         OS::PrintErr("Instance call at %" Px
                      " resetting to polymorphic dispatch, %s\n",
-                     pc, ic_data.ToCString());
+                     pc, ic_data_.ToCString());
       }
     }
   }
 #endif
 }
 
-void Bytecode::ResetICDatas(Zone* zone) const {
+void CallSiteResetter::ResetICDatas(const Bytecode& bytecode) {
   // Iterate over the Bytecode's object pool and reset all ICDatas.
-  const ObjectPool& pool = ObjectPool::Handle(zone, object_pool());
-  ASSERT(!pool.IsNull());
-  pool.ResetICDatas(zone);
+  pool_ = bytecode.object_pool();
+  ASSERT(!pool_.IsNull());
+  ResetICDatas(pool_);
 
-  Object& object = Object::Handle(zone);
-  String& name = String::Handle(zone);
-  Class& new_cls = Class::Handle(zone);
-  Library& new_lib = Library::Handle(zone);
-  Function& new_function = Function::Handle(zone);
-  Field& new_field = Field::Handle(zone);
-  for (intptr_t i = 0; i < pool.Length(); i++) {
-    ObjectPool::EntryType entry_type = pool.TypeAt(i);
+  for (intptr_t i = 0; i < pool_.Length(); i++) {
+    ObjectPool::EntryType entry_type = pool_.TypeAt(i);
     if (entry_type != ObjectPool::EntryType::kTaggedObject) {
       continue;
     }
-    object = pool.ObjectAt(i);
-    if (object.IsFunction()) {
-      const Function& old_function = Function::Cast(object);
+    object_ = pool_.ObjectAt(i);
+    if (object_.IsFunction()) {
+      const Function& old_function = Function::Cast(object_);
       if (old_function.IsClosureFunction()) {
         continue;
       }
-      name = old_function.name();
-      new_cls = old_function.Owner();
-      if (new_cls.IsTopLevel()) {
-        new_lib = new_cls.library();
-        new_function = new_lib.LookupLocalFunction(name);
+      name_ = old_function.name();
+      new_cls_ = old_function.Owner();
+      if (new_cls_.IsTopLevel()) {
+        new_lib_ = new_cls_.library();
+        new_function_ = new_lib_.LookupLocalFunction(name_);
       } else {
-        new_function = new_cls.LookupFunction(name);
+        new_function_ = new_cls_.LookupFunction(name_);
       }
-      if (!new_function.IsNull() &&
-          (new_function.is_static() == old_function.is_static()) &&
-          (new_function.kind() == old_function.kind())) {
-        pool.SetObjectAt(i, new_function);
+      if (!new_function_.IsNull() &&
+          (new_function_.is_static() == old_function.is_static()) &&
+          (new_function_.kind() == old_function.kind())) {
+        pool_.SetObjectAt(i, new_function_);
       } else {
         VTIR_Print("Cannot rebind function %s\n", old_function.ToCString());
       }
-    } else if (object.IsField()) {
-      const Field& old_field = Field::Cast(object);
-      name = old_field.name();
-      new_cls = old_field.Owner();
-      if (new_cls.IsTopLevel()) {
-        new_lib = new_cls.library();
-        new_field = new_lib.LookupLocalField(name);
+    } else if (object_.IsField()) {
+      const Field& old_field = Field::Cast(object_);
+      name_ = old_field.name();
+      new_cls_ = old_field.Owner();
+      if (new_cls_.IsTopLevel()) {
+        new_lib_ = new_cls_.library();
+        new_field_ = new_lib_.LookupLocalField(name_);
       } else {
-        new_field = new_cls.LookupField(name);
+        new_field_ = new_cls_.LookupField(name_);
       }
-      if (!new_field.IsNull() &&
-          (new_field.is_static() == old_field.is_static())) {
-        pool.SetObjectAt(i, new_field);
+      if (!new_field_.IsNull() &&
+          (new_field_.is_static() == old_field.is_static())) {
+        pool_.SetObjectAt(i, new_field_);
       } else {
         VTIR_Print("Cannot rebind field %s\n", old_field.ToCString());
       }
@@ -241,16 +229,15 @@
   }
 }
 
-void ObjectPool::ResetICDatas(Zone* zone) const {
-  Object& object = Object::Handle(zone);
-  for (intptr_t i = 0; i < Length(); i++) {
-    ObjectPool::EntryType entry_type = TypeAt(i);
+void CallSiteResetter::ResetICDatas(const ObjectPool& pool) {
+  for (intptr_t i = 0; i < pool.Length(); i++) {
+    ObjectPool::EntryType entry_type = pool.TypeAt(i);
     if (entry_type != ObjectPool::EntryType::kTaggedObject) {
       continue;
     }
-    object = ObjectAt(i);
-    if (object.IsICData()) {
-      ICData::Cast(object).Reset(zone);
+    object_ = pool.ObjectAt(i);
+    if (object_.IsICData()) {
+      Reset(ICData::Cast(object_));
     }
   }
 }
@@ -841,96 +828,90 @@
   }
 }
 
-static const Function* static_call_target = NULL;
-
-void ICData::Reset(Zone* zone) const {
-  RebindRule rule = rebind_rule();
-  if (rule == kInstance) {
-    const intptr_t num_args = NumArgsTested();
-    const bool tracking_exactness = is_tracking_exactness();
-    const intptr_t len = Length();
+void CallSiteResetter::Reset(const ICData& ic) {
+  ICData::RebindRule rule = ic.rebind_rule();
+  if (rule == ICData::kInstance) {
+    const intptr_t num_args = ic.NumArgsTested();
+    const bool tracking_exactness = ic.is_tracking_exactness();
+    const intptr_t len = ic.Length();
     // We need at least one non-sentinel entry to require a check
     // for the smi fast path case.
     if (num_args == 2 && len >= 2) {
-      if (IsImmutable()) {
+      if (ic.IsImmutable()) {
         return;
       }
-      Zone* zone = Thread::Current()->zone();
-      const String& name = String::Handle(target_name());
-      const Class& smi_class = Class::Handle(Smi::Class());
+      name_ = ic.target_name();
+      const Class& smi_class = Class::Handle(zone_, Smi::Class());
       const Function& smi_op_target = Function::Handle(
-          Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
+          zone_, Resolver::ResolveDynamicAnyArgs(zone_, smi_class, name_));
       GrowableArray<intptr_t> class_ids(2);
-      Function& target = Function::Handle();
-      GetCheckAt(0, &class_ids, &target);
+      Function& target = Function::Handle(zone_);
+      ic.GetCheckAt(0, &class_ids, &target);
       if ((target.raw() == smi_op_target.raw()) && (class_ids[0] == kSmiCid) &&
           (class_ids[1] == kSmiCid)) {
         // The smi fast path case, preserve the initial entry but reset the
         // count.
-        ClearCountAt(0);
-        WriteSentinelAt(1);
-        const Array& array = Array::Handle(entries());
-        array.Truncate(2 * TestEntryLength());
+        ic.ClearCountAt(0);
+        ic.WriteSentinelAt(1);
+        entries_ = ic.entries();
+        entries_.Truncate(2 * ic.TestEntryLength());
         return;
       }
       // Fall back to the normal behavior with cached empty ICData arrays.
     }
-    const Array& data_array = Array::Handle(
-        zone, CachedEmptyICDataArray(num_args, tracking_exactness));
-    set_entries(data_array);
-    set_is_megamorphic(false);
+    entries_ = ICData::CachedEmptyICDataArray(num_args, tracking_exactness);
+    ic.set_entries(entries_);
+    ic.set_is_megamorphic(false);
     return;
-  } else if (rule == kNoRebind || rule == kNSMDispatch) {
+  } else if (rule == ICData::kNoRebind || rule == ICData::kNSMDispatch) {
     // TODO(30877) we should account for addition/removal of NSM.
     // Don't rebind dispatchers.
     return;
-  } else if (rule == kStatic || rule == kSuper) {
-    const Function& old_target = Function::Handle(zone, GetTargetAt(0));
-    if (old_target.IsNull()) {
+  } else if (rule == ICData::kStatic || rule == ICData::kSuper) {
+    old_target_ = ic.GetTargetAt(0);
+    if (old_target_.IsNull()) {
       FATAL("old_target is NULL.\n");
     }
-    static_call_target = &old_target;
+    name_ = old_target_.name();
 
-    const String& selector = String::Handle(zone, old_target.name());
-    Function& new_target = Function::Handle(zone);
-
-    if (rule == kStatic) {
-      ASSERT(old_target.is_static() ||
-             old_target.kind() == RawFunction::kConstructor);
+    if (rule == ICData::kStatic) {
+      ASSERT(old_target_.is_static() ||
+             old_target_.kind() == RawFunction::kConstructor);
       // This can be incorrect if the call site was an unqualified invocation.
-      const Class& cls = Class::Handle(zone, old_target.Owner());
-      new_target = cls.LookupFunction(selector);
-      if (new_target.kind() != old_target.kind()) {
-        new_target = Function::null();
+      new_cls_ = old_target_.Owner();
+      new_target_ = new_cls_.LookupFunction(name_);
+      if (new_target_.kind() != old_target_.kind()) {
+        new_target_ = Function::null();
       }
     } else {
       // Super call.
-      Function& caller = Function::Handle(zone);
-      caller = Owner();
-      ASSERT(!caller.is_static());
-      Class& cls = Class::Handle(zone, caller.Owner());
-      cls = cls.SuperClass();
-      while (!cls.IsNull()) {
+      caller_ = ic.Owner();
+      ASSERT(!caller_.is_static());
+      new_cls_ = caller_.Owner();
+      new_cls_ = new_cls_.SuperClass();
+      new_target_ = Function::null();
+      while (!new_cls_.IsNull()) {
         // TODO(rmacnak): Should use Resolver::ResolveDynamicAnyArgs to handle
         // method-extractors and call-through-getters, but we're in a no
         // safepoint scope here.
-        new_target = cls.LookupDynamicFunction(selector);
-        if (!new_target.IsNull()) {
+        new_target_ = new_cls_.LookupDynamicFunction(name_);
+        if (!new_target_.IsNull()) {
           break;
         }
-        cls = cls.SuperClass();
+        new_cls_ = new_cls_.SuperClass();
       }
     }
-    const Array& args_desc_array = Array::Handle(zone, arguments_descriptor());
-    ArgumentsDescriptor args_desc(args_desc_array);
-    if (new_target.IsNull() || !new_target.AreValidArguments(args_desc, NULL)) {
+    args_desc_array_ = ic.arguments_descriptor();
+    ArgumentsDescriptor args_desc(args_desc_array_);
+    if (new_target_.IsNull() ||
+        !new_target_.AreValidArguments(args_desc, NULL)) {
       // TODO(rmacnak): Patch to a NSME stub.
       VTIR_Print("Cannot rebind static call to %s from %s\n",
-                 old_target.ToCString(),
-                 Object::Handle(zone, Owner()).ToCString());
+                 old_target_.ToCString(),
+                 Object::Handle(zone_, ic.Owner()).ToCString());
       return;
     }
-    ClearAndSetStaticTarget(new_target);
+    ic.ClearAndSetStaticTarget(new_target_);
   } else {
     FATAL("Unexpected rebind rule.");
   }
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 50a922e..7d9126c 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1352,6 +1352,7 @@
   friend class StackFrame;
   friend class Profiler;
   friend class FunctionDeserializationCluster;
+  friend class CallSiteResetter;
 };
 
 class RawBytecode : public RawObject {
diff --git a/runtime/vm/regexp_test.cc b/runtime/vm/regexp_test.cc
index f800931..5ba4056 100644
--- a/runtime/vm/regexp_test.cc
+++ b/runtime/vm/regexp_test.cc
@@ -17,7 +17,7 @@
   Zone* zone = thread->zone();
   const RegExp& regexp =
       RegExp::Handle(RegExpEngine::CreateRegExp(thread, pat, RegExpFlags()));
-  const Smi& idx = Smi::Handle(Smi::New(0));
+  const Smi& idx = Object::smi_zero();
   return IRRegExpMacroAssembler::Execute(regexp, str, idx, /*sticky=*/false,
                                          zone);
 }
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 032bfd1..4a01769 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -894,8 +894,8 @@
 
     // No source code for this assertion, set url to null.
     args.SetAt(1, String::Handle(zone, String::null()));
-    args.SetAt(2, Smi::Handle(zone, Smi::New(0)));
-    args.SetAt(3, Smi::Handle(zone, Smi::New(0)));
+    args.SetAt(2, Object::smi_zero());
+    args.SetAt(3, Object::smi_zero());
     args.SetAt(4, String::Handle(zone, String::null()));
 
     Exceptions::ThrowByType(Exceptions::kAssertion, args);