[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);