| Index: runtime/vm/object.cc
|
| ===================================================================
|
| --- runtime/vm/object.cc (revision 35455)
|
| +++ runtime/vm/object.cc (working copy)
|
| @@ -10601,6 +10601,526 @@
|
| }
|
|
|
|
|
| +const char* ICData::ToCString() const {
|
| + const char* kFormat = "ICData target:'%s' num-args: %" Pd
|
| + " num-checks: %" Pd "";
|
| + const String& name = String::Handle(target_name());
|
| + const intptr_t num_args = NumArgsTested();
|
| + const intptr_t num_checks = NumberOfChecks();
|
| + intptr_t len = OS::SNPrint(NULL, 0, kFormat, name.ToCString(),
|
| + num_args, num_checks) + 1;
|
| + char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
|
| + OS::SNPrint(chars, len, kFormat, name.ToCString(), num_args, num_checks);
|
| + return chars;
|
| +}
|
| +
|
| +
|
| +void ICData::set_owner(const Function& value) const {
|
| + ASSERT(!value.IsNull());
|
| + StorePointer(&raw_ptr()->owner_, value.raw());
|
| +}
|
| +
|
| +
|
| +void ICData::set_target_name(const String& value) const {
|
| + ASSERT(!value.IsNull());
|
| + StorePointer(&raw_ptr()->target_name_, value.raw());
|
| +}
|
| +
|
| +
|
| +void ICData::set_arguments_descriptor(const Array& value) const {
|
| + ASSERT(!value.IsNull());
|
| + StorePointer(&raw_ptr()->args_descriptor_, value.raw());
|
| +}
|
| +
|
| +void ICData::set_deopt_id(intptr_t value) const {
|
| + ASSERT(value <= kMaxInt32);
|
| + raw_ptr()->deopt_id_ = value;
|
| +}
|
| +
|
| +
|
| +void ICData::set_ic_data(const Array& value) const {
|
| + ASSERT(!value.IsNull());
|
| + StorePointer(&raw_ptr()->ic_data_, value.raw());
|
| +}
|
| +
|
| +
|
| +intptr_t ICData::NumArgsTested() const {
|
| + return NumArgsTestedBits::decode(raw_ptr()->state_bits_);
|
| +}
|
| +
|
| +
|
| +void ICData::SetNumArgsTested(intptr_t value) const {
|
| + ASSERT(Utils::IsUint(2, value));
|
| + raw_ptr()->state_bits_ =
|
| + NumArgsTestedBits::update(value, raw_ptr()->state_bits_);
|
| +}
|
| +
|
| +
|
| +uint32_t ICData::DeoptReasons() const {
|
| + return DeoptReasonBits::decode(raw_ptr()->state_bits_);
|
| +}
|
| +
|
| +
|
| +void ICData::SetDeoptReasons(uint32_t reasons) const {
|
| + raw_ptr()->state_bits_ =
|
| + DeoptReasonBits::update(reasons, raw_ptr()->state_bits_);
|
| +}
|
| +
|
| +
|
| +bool ICData::HasDeoptReason(DeoptReasonId reason) const {
|
| + return (DeoptReasons() & (1 << reason)) != 0;
|
| +}
|
| +
|
| +
|
| +void ICData::AddDeoptReason(DeoptReasonId reason) const {
|
| + SetDeoptReasons(DeoptReasons() | (1 << reason));
|
| +}
|
| +
|
| +
|
| +bool ICData::IssuedJSWarning() const {
|
| + return IssuedJSWarningBit::decode(raw_ptr()->state_bits_);
|
| +}
|
| +
|
| +
|
| +void ICData::SetIssuedJSWarning() const {
|
| + raw_ptr()->state_bits_ =
|
| + IssuedJSWarningBit::update(true, raw_ptr()->state_bits_);
|
| +}
|
| +
|
| +
|
| +bool ICData::IsClosureCall() const {
|
| + return IsClosureCallBit::decode(raw_ptr()->state_bits_);
|
| +}
|
| +
|
| +
|
| +void ICData::SetIsClosureCall() const {
|
| + raw_ptr()->state_bits_ =
|
| + IsClosureCallBit::update(true, raw_ptr()->state_bits_);
|
| +}
|
| +
|
| +
|
| +void ICData::set_state_bits(uint32_t bits) const {
|
| + raw_ptr()->state_bits_ = bits;
|
| +}
|
| +
|
| +
|
| +intptr_t ICData::TestEntryLengthFor(intptr_t num_args) {
|
| + return num_args + 1 /* target function*/ + 1 /* frequency */;
|
| +}
|
| +
|
| +
|
| +intptr_t ICData::TestEntryLength() const {
|
| + return TestEntryLengthFor(NumArgsTested());
|
| +}
|
| +
|
| +
|
| +intptr_t ICData::NumberOfChecks() const {
|
| + // Do not count the sentinel;
|
| + return (Smi::Value(ic_data()->ptr()->length_) / TestEntryLength()) - 1;
|
| +}
|
| +
|
| +
|
| +void ICData::WriteSentinel(const Array& data) const {
|
| + ASSERT(!data.IsNull());
|
| + for (intptr_t i = 1; i <= TestEntryLength(); i++) {
|
| + data.SetAt(data.Length() - i, smi_illegal_cid());
|
| + }
|
| +}
|
| +
|
| +
|
| +#if defined(DEBUG)
|
| +// Used in asserts to verify that a check is not added twice.
|
| +bool ICData::HasCheck(const GrowableArray<intptr_t>& cids) const {
|
| + const intptr_t len = NumberOfChecks();
|
| + for (intptr_t i = 0; i < len; i++) {
|
| + GrowableArray<intptr_t> class_ids;
|
| + Function& target = Function::Handle();
|
| + GetCheckAt(i, &class_ids, &target);
|
| + bool matches = true;
|
| + for (intptr_t k = 0; k < class_ids.length(); k++) {
|
| + if (class_ids[k] != cids[k]) {
|
| + matches = false;
|
| + break;
|
| + }
|
| + }
|
| + if (matches) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +#endif // DEBUG
|
| +
|
| +
|
| +// Used for unoptimized static calls when no class-ids are checked.
|
| +void ICData::AddTarget(const Function& target) const {
|
| + ASSERT(!target.IsNull());
|
| + if (NumArgsTested() > 0) {
|
| + // Create a fake cid entry, so that we can store the target.
|
| + GrowableArray<intptr_t> class_ids(NumArgsTested());
|
| + for (intptr_t i = 0; i < NumArgsTested(); i++) {
|
| + class_ids.Add(kObjectCid);
|
| + }
|
| + AddCheck(class_ids, target);
|
| + return;
|
| + }
|
| + ASSERT(NumArgsTested() >= 0);
|
| + // Can add only once.
|
| + const intptr_t old_num = NumberOfChecks();
|
| + ASSERT(old_num == 0);
|
| + Array& data = Array::Handle(ic_data());
|
| + const intptr_t new_len = data.Length() + TestEntryLength();
|
| + data = Array::Grow(data, new_len, Heap::kOld);
|
| + set_ic_data(data);
|
| + WriteSentinel(data);
|
| + intptr_t data_pos = old_num * TestEntryLength();
|
| + ASSERT(!target.IsNull());
|
| + data.SetAt(data_pos++, target);
|
| + const Smi& value = Smi::Handle(Smi::New(0));
|
| + data.SetAt(data_pos, value);
|
| +}
|
| +
|
| +
|
| +void ICData::AddCheck(const GrowableArray<intptr_t>& class_ids,
|
| + const Function& target) const {
|
| + ASSERT(!target.IsNull());
|
| + DEBUG_ASSERT(!HasCheck(class_ids));
|
| + ASSERT(NumArgsTested() > 1); // Otherwise use 'AddReceiverCheck'.
|
| + ASSERT(class_ids.length() == NumArgsTested());
|
| + const intptr_t old_num = NumberOfChecks();
|
| + Array& data = Array::Handle(ic_data());
|
| + // ICData of static calls with NumArgsTested() > 0 have initially a
|
| + // dummy set of cids entered (see ICData::AddTarget). That entry is
|
| + // overwritten by first real type feedback data.
|
| + if (old_num == 1) {
|
| + bool has_dummy_entry = true;
|
| + for (intptr_t i = 0; i < NumArgsTested(); i++) {
|
| + if (Smi::Value(Smi::RawCast(data.At(i))) != kObjectCid) {
|
| + has_dummy_entry = false;
|
| + break;
|
| + }
|
| + }
|
| + if (has_dummy_entry) {
|
| + ASSERT(target.raw() == data.At(NumArgsTested()));
|
| + // Replace dummy entry.
|
| + Smi& value = Smi::Handle();
|
| + for (intptr_t i = 0; i < NumArgsTested(); i++) {
|
| + ASSERT(class_ids[i] != kIllegalCid);
|
| + value = Smi::New(class_ids[i]);
|
| + data.SetAt(i, value);
|
| + }
|
| + return;
|
| + }
|
| + }
|
| + const intptr_t new_len = data.Length() + TestEntryLength();
|
| + data = Array::Grow(data, new_len, Heap::kOld);
|
| + set_ic_data(data);
|
| + WriteSentinel(data);
|
| + intptr_t data_pos = old_num * TestEntryLength();
|
| + Smi& value = Smi::Handle();
|
| + for (intptr_t i = 0; i < class_ids.length(); i++) {
|
| + // kIllegalCid is used as terminating value, do not add it.
|
| + ASSERT(class_ids[i] != kIllegalCid);
|
| + value = Smi::New(class_ids[i]);
|
| + data.SetAt(data_pos++, value);
|
| + }
|
| + ASSERT(!target.IsNull());
|
| + data.SetAt(data_pos++, target);
|
| + value = Smi::New(1);
|
| + data.SetAt(data_pos, value);
|
| +}
|
| +
|
| +
|
| +void ICData::AddReceiverCheck(intptr_t receiver_class_id,
|
| + const Function& target,
|
| + intptr_t count) const {
|
| +#if defined(DEBUG)
|
| + GrowableArray<intptr_t> class_ids(1);
|
| + class_ids.Add(receiver_class_id);
|
| + ASSERT(!HasCheck(class_ids));
|
| +#endif // DEBUG
|
| + ASSERT(!target.IsNull());
|
| + ASSERT(NumArgsTested() == 1); // Otherwise use 'AddCheck'.
|
| + ASSERT(receiver_class_id != kIllegalCid);
|
| +
|
| + const intptr_t old_num = NumberOfChecks();
|
| + Array& data = Array::Handle(ic_data());
|
| + const intptr_t new_len = data.Length() + TestEntryLength();
|
| + data = Array::Grow(data, new_len, Heap::kOld);
|
| + set_ic_data(data);
|
| + WriteSentinel(data);
|
| + intptr_t data_pos = old_num * TestEntryLength();
|
| + if ((receiver_class_id == kSmiCid) && (data_pos > 0)) {
|
| + ASSERT(GetReceiverClassIdAt(0) != kSmiCid);
|
| + // Move class occupying position 0 to the data_pos.
|
| + for (intptr_t i = 0; i < TestEntryLength(); i++) {
|
| + data.SetAt(data_pos + i, Object::Handle(data.At(i)));
|
| + }
|
| + // Insert kSmiCid in position 0.
|
| + data_pos = 0;
|
| + }
|
| + data.SetAt(data_pos, Smi::Handle(Smi::New(receiver_class_id)));
|
| + data.SetAt(data_pos + 1, target);
|
| + data.SetAt(data_pos + 2, Smi::Handle(Smi::New(count)));
|
| +}
|
| +
|
| +
|
| +void ICData::GetCheckAt(intptr_t index,
|
| + GrowableArray<intptr_t>* class_ids,
|
| + Function* target) const {
|
| + ASSERT(index < NumberOfChecks());
|
| + ASSERT(class_ids != NULL);
|
| + ASSERT(target != NULL);
|
| + class_ids->Clear();
|
| + const Array& data = Array::Handle(ic_data());
|
| + 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++))));
|
| + }
|
| + (*target) ^= data.At(data_pos++);
|
| +}
|
| +
|
| +
|
| +void ICData::GetOneClassCheckAt(intptr_t index,
|
| + intptr_t* class_id,
|
| + Function* target) const {
|
| + ASSERT(class_id != NULL);
|
| + ASSERT(target != NULL);
|
| + ASSERT(NumArgsTested() == 1);
|
| + const Array& data = Array::Handle(ic_data());
|
| + const intptr_t data_pos = index * TestEntryLength();
|
| + *class_id = Smi::Value(Smi::RawCast(data.At(data_pos)));
|
| + *target ^= data.At(data_pos + 1);
|
| +}
|
| +
|
| +
|
| +intptr_t ICData::GetCidAt(intptr_t index) const {
|
| + ASSERT(NumArgsTested() == 1);
|
| + const Array& data = Array::Handle(ic_data());
|
| + const intptr_t data_pos = index * TestEntryLength();
|
| + return Smi::Value(Smi::RawCast(data.At(data_pos)));
|
| +}
|
| +
|
| +
|
| +intptr_t ICData::GetClassIdAt(intptr_t index, intptr_t arg_nr) const {
|
| + GrowableArray<intptr_t> class_ids;
|
| + Function& target = Function::Handle();
|
| + GetCheckAt(index, &class_ids, &target);
|
| + return class_ids[arg_nr];
|
| +}
|
| +
|
| +
|
| +intptr_t ICData::GetReceiverClassIdAt(intptr_t index) const {
|
| + ASSERT(index < NumberOfChecks());
|
| + const Array& data = Array::Handle(ic_data());
|
| + const intptr_t data_pos = index * TestEntryLength();
|
| + return Smi::Value(Smi::RawCast(data.At(data_pos)));
|
| +}
|
| +
|
| +
|
| +RawFunction* ICData::GetTargetAt(intptr_t index) const {
|
| + const intptr_t data_pos = index * TestEntryLength() + NumArgsTested();
|
| + ASSERT(Object::Handle(Array::Handle(ic_data()).At(data_pos)).IsFunction());
|
| +
|
| + NoGCScope no_gc;
|
| + RawArray* raw_data = ic_data();
|
| + return reinterpret_cast<RawFunction*>(raw_data->ptr()->data()[data_pos]);
|
| +}
|
| +
|
| +
|
| +void ICData::IncrementCountAt(intptr_t index, intptr_t value) const {
|
| + ASSERT(0 <= value);
|
| + ASSERT(value <= Smi::kMaxValue);
|
| + SetCountAt(index, Utils::Minimum(GetCountAt(index) + value, Smi::kMaxValue));
|
| +}
|
| +
|
| +
|
| +void ICData::SetCountAt(intptr_t index, intptr_t value) const {
|
| + ASSERT(0 <= value);
|
| + ASSERT(value <= Smi::kMaxValue);
|
| +
|
| + const Array& data = Array::Handle(ic_data());
|
| + const intptr_t data_pos = index * TestEntryLength() +
|
| + CountIndexFor(NumArgsTested());
|
| + data.SetAt(data_pos, Smi::Handle(Smi::New(value)));
|
| +}
|
| +
|
| +
|
| +intptr_t ICData::GetCountAt(intptr_t index) const {
|
| + const Array& data = Array::Handle(ic_data());
|
| + const intptr_t data_pos = index * TestEntryLength() +
|
| + CountIndexFor(NumArgsTested());
|
| + return Smi::Value(Smi::RawCast(data.At(data_pos)));
|
| +}
|
| +
|
| +
|
| +intptr_t ICData::AggregateCount() const {
|
| + if (IsNull()) return 0;
|
| + const intptr_t len = NumberOfChecks();
|
| + intptr_t count = 0;
|
| + for (intptr_t i = 0; i < len; i++) {
|
| + count += GetCountAt(i);
|
| + }
|
| + return count;
|
| +}
|
| +
|
| +
|
| +RawFunction* ICData::GetTargetForReceiverClassId(intptr_t class_id) const {
|
| + const intptr_t len = NumberOfChecks();
|
| + for (intptr_t i = 0; i < len; i++) {
|
| + if (GetReceiverClassIdAt(i) == class_id) {
|
| + return GetTargetAt(i);
|
| + }
|
| + }
|
| + return Function::null();
|
| +}
|
| +
|
| +
|
| +RawICData* ICData::AsUnaryClassChecksForArgNr(intptr_t arg_nr) const {
|
| + ASSERT(!IsNull());
|
| + ASSERT(NumArgsTested() > arg_nr);
|
| + if ((arg_nr == 0) && (NumArgsTested() == 1)) {
|
| + // Frequent case.
|
| + return raw();
|
| + }
|
| + const intptr_t kNumArgsTested = 1;
|
| + ICData& result = ICData::Handle(ICData::New(
|
| + Function::Handle(owner()),
|
| + String::Handle(target_name()),
|
| + Array::Handle(arguments_descriptor()),
|
| + deopt_id(),
|
| + kNumArgsTested));
|
| + const intptr_t len = NumberOfChecks();
|
| + for (intptr_t i = 0; i < len; i++) {
|
| + const intptr_t class_id = GetClassIdAt(i, arg_nr);
|
| + const intptr_t count = GetCountAt(i);
|
| + intptr_t duplicate_class_id = -1;
|
| + const intptr_t result_len = result.NumberOfChecks();
|
| + for (intptr_t k = 0; k < result_len; k++) {
|
| + if (class_id == result.GetReceiverClassIdAt(k)) {
|
| + duplicate_class_id = k;
|
| + break;
|
| + }
|
| + }
|
| + if (duplicate_class_id >= 0) {
|
| + // This check is valid only when checking the receiver.
|
| + ASSERT((arg_nr != 0) ||
|
| + (result.GetTargetAt(duplicate_class_id) == GetTargetAt(i)));
|
| + result.IncrementCountAt(duplicate_class_id, count);
|
| + } else {
|
| + // This will make sure that Smi is first if it exists.
|
| + result.AddReceiverCheck(class_id,
|
| + Function::Handle(GetTargetAt(i)),
|
| + count);
|
| + }
|
| + }
|
| + // Copy deoptimization reasons.
|
| + result.SetDeoptReasons(DeoptReasons());
|
| +
|
| + return result.raw();
|
| +}
|
| +
|
| +
|
| +bool ICData::AllTargetsHaveSameOwner(intptr_t owner_cid) const {
|
| + if (NumberOfChecks() == 0) return false;
|
| + Class& cls = Class::Handle();
|
| + const intptr_t len = NumberOfChecks();
|
| + for (intptr_t i = 0; i < len; i++) {
|
| + cls = Function::Handle(GetTargetAt(i)).Owner();
|
| + if (cls.id() != owner_cid) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +bool ICData::AllReceiversAreNumbers() const {
|
| + if (NumberOfChecks() == 0) return false;
|
| + Class& cls = Class::Handle();
|
| + const intptr_t len = NumberOfChecks();
|
| + for (intptr_t i = 0; i < len; i++) {
|
| + cls = Function::Handle(GetTargetAt(i)).Owner();
|
| + const intptr_t cid = cls.id();
|
| + if ((cid != kSmiCid) &&
|
| + (cid != kMintCid) &&
|
| + (cid != kBigintCid) &&
|
| + (cid != kDoubleCid)) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +bool ICData::HasReceiverClassId(intptr_t class_id) const {
|
| + ASSERT(NumArgsTested() > 0);
|
| + const intptr_t len = NumberOfChecks();
|
| + for (intptr_t i = 0; i < len; i++) {
|
| + const intptr_t test_class_id = GetReceiverClassIdAt(i);
|
| + if (test_class_id == class_id) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +
|
| +// Returns true if all targets are the same.
|
| +// TODO(srdjan): if targets are native use their C_function to compare.
|
| +bool ICData::HasOneTarget() const {
|
| + ASSERT(NumberOfChecks() > 0);
|
| + const Function& first_target = Function::Handle(GetTargetAt(0));
|
| + const intptr_t len = NumberOfChecks();
|
| + for (intptr_t i = 1; i < len; i++) {
|
| + if (GetTargetAt(i) != first_target.raw()) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +RawICData* ICData::New(const Function& owner,
|
| + const String& target_name,
|
| + const Array& arguments_descriptor,
|
| + intptr_t deopt_id,
|
| + intptr_t num_args_tested) {
|
| + ASSERT(!owner.IsNull());
|
| + ASSERT(!target_name.IsNull());
|
| + ASSERT(!arguments_descriptor.IsNull());
|
| + ASSERT(Object::icdata_class() != Class::null());
|
| + ASSERT(num_args_tested >= 0);
|
| + ICData& result = ICData::Handle();
|
| + {
|
| + // IC data objects are long living objects, allocate them in old generation.
|
| + RawObject* raw = Object::Allocate(ICData::kClassId,
|
| + ICData::InstanceSize(),
|
| + Heap::kOld);
|
| + NoGCScope no_gc;
|
| + result ^= raw;
|
| + }
|
| + result.set_owner(owner);
|
| + result.set_target_name(target_name);
|
| + result.set_arguments_descriptor(arguments_descriptor);
|
| + result.set_deopt_id(deopt_id);
|
| + result.set_state_bits(0);
|
| + result.SetNumArgsTested(num_args_tested);
|
| + // Number of array elements in one test entry.
|
| + intptr_t len = result.TestEntryLength();
|
| + // IC data array must be null terminated (sentinel entry).
|
| + const Array& ic_data = Array::Handle(Array::New(len, Heap::kOld));
|
| + result.set_ic_data(ic_data);
|
| + result.WriteSentinel(ic_data);
|
| + return result.raw();
|
| +}
|
| +
|
| +
|
| +void ICData::PrintToJSONStream(JSONStream* stream, bool ref) const {
|
| + Object::PrintToJSONStream(stream, ref);
|
| +}
|
| +
|
| +
|
| Code::Comments& Code::Comments::New(intptr_t count) {
|
| Comments* comments;
|
| if (count < 0 || count > (kIntptrMax / kNumberOfEntries)) {
|
| @@ -10708,7 +11228,8 @@
|
| }
|
|
|
|
|
| -RawDeoptInfo* Code::GetDeoptInfoAtPc(uword pc, intptr_t* deopt_reason) const {
|
| +RawDeoptInfo* Code::GetDeoptInfoAtPc(
|
| + uword pc, ICData::DeoptReasonId* deopt_reason) const {
|
| ASSERT(is_optimized());
|
| const Instructions& instrs = Instructions::Handle(instructions());
|
| uword code_entry = instrs.EntryPoint();
|
| @@ -10723,11 +11244,13 @@
|
| DeoptTable::GetEntry(table, i, &offset, &info, &reason);
|
| if (pc == (code_entry + offset.Value())) {
|
| ASSERT(!info.IsNull());
|
| - *deopt_reason = reason.Value();
|
| + ASSERT((0 <= reason.Value()) &&
|
| + (reason.Value() < ICData::kDeoptNumReasons));
|
| + *deopt_reason = static_cast<ICData::DeoptReasonId>(reason.Value());
|
| return info.raw();
|
| }
|
| }
|
| - *deopt_reason = kDeoptUnknown;
|
| + *deopt_reason = ICData::kDeoptUnknown;
|
| return DeoptInfo::null();
|
| }
|
|
|
| @@ -11413,480 +11936,6 @@
|
| }
|
|
|
|
|
| -const char* ICData::ToCString() const {
|
| - const char* kFormat = "ICData target:'%s' num-args: %" Pd
|
| - " num-checks: %" Pd "";
|
| - const String& name = String::Handle(target_name());
|
| - const intptr_t num_args = num_args_tested();
|
| - const intptr_t num_checks = NumberOfChecks();
|
| - intptr_t len = OS::SNPrint(NULL, 0, kFormat, name.ToCString(),
|
| - num_args, num_checks) + 1;
|
| - char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
|
| - OS::SNPrint(chars, len, kFormat, name.ToCString(), num_args, num_checks);
|
| - return chars;
|
| -}
|
| -
|
| -
|
| -void ICData::set_function(const Function& value) const {
|
| - ASSERT(!value.IsNull());
|
| - StorePointer(&raw_ptr()->function_, value.raw());
|
| -}
|
| -
|
| -
|
| -void ICData::set_target_name(const String& value) const {
|
| - ASSERT(!value.IsNull());
|
| - StorePointer(&raw_ptr()->target_name_, value.raw());
|
| -}
|
| -
|
| -
|
| -void ICData::set_arguments_descriptor(const Array& value) const {
|
| - ASSERT(!value.IsNull());
|
| - StorePointer(&raw_ptr()->args_descriptor_, value.raw());
|
| -}
|
| -
|
| -void ICData::set_deopt_id(intptr_t value) const {
|
| - raw_ptr()->deopt_id_ = value;
|
| -}
|
| -
|
| -
|
| -void ICData::set_num_args_tested(intptr_t value) const {
|
| - raw_ptr()->num_args_tested_ = value;
|
| -}
|
| -
|
| -
|
| -void ICData::set_ic_data(const Array& value) const {
|
| - ASSERT(!value.IsNull());
|
| - StorePointer(&raw_ptr()->ic_data_, value.raw());
|
| -}
|
| -
|
| -
|
| -void ICData::set_deopt_reason(intptr_t deopt_reason) const {
|
| - raw_ptr()->deopt_reason_ = deopt_reason;
|
| -}
|
| -
|
| -void ICData::set_is_closure_call(bool value) const {
|
| - raw_ptr()->is_closure_call_ = value ? 1 : 0;
|
| -}
|
| -
|
| -
|
| -intptr_t ICData::TestEntryLengthFor(intptr_t num_args) {
|
| - return num_args + 1 /* target function*/ + 1 /* frequency */;
|
| -}
|
| -
|
| -
|
| -intptr_t ICData::TestEntryLength() const {
|
| - return TestEntryLengthFor(num_args_tested());
|
| -}
|
| -
|
| -
|
| -intptr_t ICData::NumberOfChecks() const {
|
| - // Do not count the sentinel;
|
| - return (Smi::Value(ic_data()->ptr()->length_) / TestEntryLength()) - 1;
|
| -}
|
| -
|
| -
|
| -void ICData::WriteSentinel(const Array& data) const {
|
| - ASSERT(!data.IsNull());
|
| - for (intptr_t i = 1; i <= TestEntryLength(); i++) {
|
| - data.SetAt(data.Length() - i, smi_illegal_cid());
|
| - }
|
| -}
|
| -
|
| -
|
| -#if defined(DEBUG)
|
| -// Used in asserts to verify that a check is not added twice.
|
| -bool ICData::HasCheck(const GrowableArray<intptr_t>& cids) const {
|
| - const intptr_t len = NumberOfChecks();
|
| - for (intptr_t i = 0; i < len; i++) {
|
| - GrowableArray<intptr_t> class_ids;
|
| - Function& target = Function::Handle();
|
| - GetCheckAt(i, &class_ids, &target);
|
| - bool matches = true;
|
| - for (intptr_t k = 0; k < class_ids.length(); k++) {
|
| - if (class_ids[k] != cids[k]) {
|
| - matches = false;
|
| - break;
|
| - }
|
| - }
|
| - if (matches) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -#endif // DEBUG
|
| -
|
| -
|
| -// Used for unoptimized static calls when no class-ids are checked.
|
| -void ICData::AddTarget(const Function& target) const {
|
| - ASSERT(!target.IsNull());
|
| - if (num_args_tested() > 0) {
|
| - // Create a fake cid entry, so that we can store the target.
|
| - GrowableArray<intptr_t> class_ids(num_args_tested());
|
| - for (intptr_t i = 0; i < num_args_tested(); i++) {
|
| - class_ids.Add(kObjectCid);
|
| - }
|
| - AddCheck(class_ids, target);
|
| - return;
|
| - }
|
| - ASSERT(num_args_tested() >= 0);
|
| - // Can add only once.
|
| - const intptr_t old_num = NumberOfChecks();
|
| - ASSERT(old_num == 0);
|
| - Array& data = Array::Handle(ic_data());
|
| - const intptr_t new_len = data.Length() + TestEntryLength();
|
| - data = Array::Grow(data, new_len, Heap::kOld);
|
| - set_ic_data(data);
|
| - WriteSentinel(data);
|
| - intptr_t data_pos = old_num * TestEntryLength();
|
| - ASSERT(!target.IsNull());
|
| - data.SetAt(data_pos++, target);
|
| - const Smi& value = Smi::Handle(Smi::New(0));
|
| - data.SetAt(data_pos, value);
|
| -}
|
| -
|
| -
|
| -void ICData::AddCheck(const GrowableArray<intptr_t>& class_ids,
|
| - const Function& target) const {
|
| - ASSERT(!target.IsNull());
|
| - DEBUG_ASSERT(!HasCheck(class_ids));
|
| - ASSERT(num_args_tested() > 1); // Otherwise use 'AddReceiverCheck'.
|
| - ASSERT(class_ids.length() == num_args_tested());
|
| - const intptr_t old_num = NumberOfChecks();
|
| - Array& data = Array::Handle(ic_data());
|
| - // ICData of static calls with num_args_tested() > 0 have initially a
|
| - // dummy set of cids entered (see ICData::AddTarget). That entry is
|
| - // overwritten by first real type feedback data.
|
| - if (old_num == 1) {
|
| - bool has_dummy_entry = true;
|
| - for (intptr_t i = 0; i < num_args_tested(); i++) {
|
| - if (Smi::Value(Smi::RawCast(data.At(i))) != kObjectCid) {
|
| - has_dummy_entry = false;
|
| - break;
|
| - }
|
| - }
|
| - if (has_dummy_entry) {
|
| - ASSERT(target.raw() == data.At(num_args_tested()));
|
| - // Replace dummy entry.
|
| - Smi& value = Smi::Handle();
|
| - for (intptr_t i = 0; i < num_args_tested(); i++) {
|
| - ASSERT(class_ids[i] != kIllegalCid);
|
| - value = Smi::New(class_ids[i]);
|
| - data.SetAt(i, value);
|
| - }
|
| - return;
|
| - }
|
| - }
|
| - const intptr_t new_len = data.Length() + TestEntryLength();
|
| - data = Array::Grow(data, new_len, Heap::kOld);
|
| - set_ic_data(data);
|
| - WriteSentinel(data);
|
| - intptr_t data_pos = old_num * TestEntryLength();
|
| - Smi& value = Smi::Handle();
|
| - for (intptr_t i = 0; i < class_ids.length(); i++) {
|
| - // kIllegalCid is used as terminating value, do not add it.
|
| - ASSERT(class_ids[i] != kIllegalCid);
|
| - value = Smi::New(class_ids[i]);
|
| - data.SetAt(data_pos++, value);
|
| - }
|
| - ASSERT(!target.IsNull());
|
| - data.SetAt(data_pos++, target);
|
| - value = Smi::New(1);
|
| - data.SetAt(data_pos, value);
|
| -}
|
| -
|
| -
|
| -void ICData::AddReceiverCheck(intptr_t receiver_class_id,
|
| - const Function& target,
|
| - intptr_t count) const {
|
| -#if defined(DEBUG)
|
| - GrowableArray<intptr_t> class_ids(1);
|
| - class_ids.Add(receiver_class_id);
|
| - ASSERT(!HasCheck(class_ids));
|
| -#endif // DEBUG
|
| - ASSERT(!target.IsNull());
|
| - ASSERT(num_args_tested() == 1); // Otherwise use 'AddCheck'.
|
| - ASSERT(receiver_class_id != kIllegalCid);
|
| -
|
| - const intptr_t old_num = NumberOfChecks();
|
| - Array& data = Array::Handle(ic_data());
|
| - const intptr_t new_len = data.Length() + TestEntryLength();
|
| - data = Array::Grow(data, new_len, Heap::kOld);
|
| - set_ic_data(data);
|
| - WriteSentinel(data);
|
| - intptr_t data_pos = old_num * TestEntryLength();
|
| - if ((receiver_class_id == kSmiCid) && (data_pos > 0)) {
|
| - ASSERT(GetReceiverClassIdAt(0) != kSmiCid);
|
| - // Move class occupying position 0 to the data_pos.
|
| - for (intptr_t i = 0; i < TestEntryLength(); i++) {
|
| - data.SetAt(data_pos + i, Object::Handle(data.At(i)));
|
| - }
|
| - // Insert kSmiCid in position 0.
|
| - data_pos = 0;
|
| - }
|
| - data.SetAt(data_pos, Smi::Handle(Smi::New(receiver_class_id)));
|
| - data.SetAt(data_pos + 1, target);
|
| - data.SetAt(data_pos + 2, Smi::Handle(Smi::New(count)));
|
| -}
|
| -
|
| -
|
| -void ICData::GetCheckAt(intptr_t index,
|
| - GrowableArray<intptr_t>* class_ids,
|
| - Function* target) const {
|
| - ASSERT(index < NumberOfChecks());
|
| - ASSERT(class_ids != NULL);
|
| - ASSERT(target != NULL);
|
| - class_ids->Clear();
|
| - const Array& data = Array::Handle(ic_data());
|
| - intptr_t data_pos = index * TestEntryLength();
|
| - for (intptr_t i = 0; i < num_args_tested(); i++) {
|
| - class_ids->Add(Smi::Value(Smi::RawCast(data.At(data_pos++))));
|
| - }
|
| - (*target) ^= data.At(data_pos++);
|
| -}
|
| -
|
| -
|
| -void ICData::GetOneClassCheckAt(intptr_t index,
|
| - intptr_t* class_id,
|
| - Function* target) const {
|
| - ASSERT(class_id != NULL);
|
| - ASSERT(target != NULL);
|
| - ASSERT(num_args_tested() == 1);
|
| - const Array& data = Array::Handle(ic_data());
|
| - const intptr_t data_pos = index * TestEntryLength();
|
| - *class_id = Smi::Value(Smi::RawCast(data.At(data_pos)));
|
| - *target ^= data.At(data_pos + 1);
|
| -}
|
| -
|
| -
|
| -intptr_t ICData::GetCidAt(intptr_t index) const {
|
| - ASSERT(num_args_tested() == 1);
|
| - const Array& data = Array::Handle(ic_data());
|
| - const intptr_t data_pos = index * TestEntryLength();
|
| - return Smi::Value(Smi::RawCast(data.At(data_pos)));
|
| -}
|
| -
|
| -
|
| -intptr_t ICData::GetClassIdAt(intptr_t index, intptr_t arg_nr) const {
|
| - GrowableArray<intptr_t> class_ids;
|
| - Function& target = Function::Handle();
|
| - GetCheckAt(index, &class_ids, &target);
|
| - return class_ids[arg_nr];
|
| -}
|
| -
|
| -
|
| -intptr_t ICData::GetReceiverClassIdAt(intptr_t index) const {
|
| - ASSERT(index < NumberOfChecks());
|
| - const Array& data = Array::Handle(ic_data());
|
| - const intptr_t data_pos = index * TestEntryLength();
|
| - return Smi::Value(Smi::RawCast(data.At(data_pos)));
|
| -}
|
| -
|
| -
|
| -RawFunction* ICData::GetTargetAt(intptr_t index) const {
|
| - const intptr_t data_pos = index * TestEntryLength() + num_args_tested();
|
| - ASSERT(Object::Handle(Array::Handle(ic_data()).At(data_pos)).IsFunction());
|
| -
|
| - NoGCScope no_gc;
|
| - RawArray* raw_data = ic_data();
|
| - return reinterpret_cast<RawFunction*>(raw_data->ptr()->data()[data_pos]);
|
| -}
|
| -
|
| -
|
| -void ICData::IncrementCountAt(intptr_t index, intptr_t value) const {
|
| - ASSERT(0 <= value);
|
| - ASSERT(value <= Smi::kMaxValue);
|
| - SetCountAt(index, Utils::Minimum(GetCountAt(index) + value, Smi::kMaxValue));
|
| -}
|
| -
|
| -
|
| -void ICData::SetCountAt(intptr_t index, intptr_t value) const {
|
| - ASSERT(0 <= value);
|
| - ASSERT(value <= Smi::kMaxValue);
|
| -
|
| - const Array& data = Array::Handle(ic_data());
|
| - const intptr_t data_pos = index * TestEntryLength() +
|
| - CountIndexFor(num_args_tested());
|
| - data.SetAt(data_pos, Smi::Handle(Smi::New(value)));
|
| -}
|
| -
|
| -
|
| -intptr_t ICData::GetCountAt(intptr_t index) const {
|
| - const Array& data = Array::Handle(ic_data());
|
| - const intptr_t data_pos = index * TestEntryLength() +
|
| - CountIndexFor(num_args_tested());
|
| - return Smi::Value(Smi::RawCast(data.At(data_pos)));
|
| -}
|
| -
|
| -
|
| -intptr_t ICData::AggregateCount() const {
|
| - if (IsNull()) return 0;
|
| - const intptr_t len = NumberOfChecks();
|
| - intptr_t count = 0;
|
| - for (intptr_t i = 0; i < len; i++) {
|
| - count += GetCountAt(i);
|
| - }
|
| - return count;
|
| -}
|
| -
|
| -
|
| -RawFunction* ICData::GetTargetForReceiverClassId(intptr_t class_id) const {
|
| - const intptr_t len = NumberOfChecks();
|
| - for (intptr_t i = 0; i < len; i++) {
|
| - if (GetReceiverClassIdAt(i) == class_id) {
|
| - return GetTargetAt(i);
|
| - }
|
| - }
|
| - return Function::null();
|
| -}
|
| -
|
| -
|
| -RawICData* ICData::AsUnaryClassChecksForArgNr(intptr_t arg_nr) const {
|
| - ASSERT(!IsNull());
|
| - ASSERT(num_args_tested() > arg_nr);
|
| - if ((arg_nr == 0) && (num_args_tested() == 1)) {
|
| - // Frequent case.
|
| - return raw();
|
| - }
|
| - const intptr_t kNumArgsTested = 1;
|
| - ICData& result = ICData::Handle(ICData::New(
|
| - Function::Handle(function()),
|
| - String::Handle(target_name()),
|
| - Array::Handle(arguments_descriptor()),
|
| - deopt_id(),
|
| - kNumArgsTested));
|
| - const intptr_t len = NumberOfChecks();
|
| - for (intptr_t i = 0; i < len; i++) {
|
| - const intptr_t class_id = GetClassIdAt(i, arg_nr);
|
| - const intptr_t count = GetCountAt(i);
|
| - intptr_t duplicate_class_id = -1;
|
| - const intptr_t result_len = result.NumberOfChecks();
|
| - for (intptr_t k = 0; k < result_len; k++) {
|
| - if (class_id == result.GetReceiverClassIdAt(k)) {
|
| - duplicate_class_id = k;
|
| - break;
|
| - }
|
| - }
|
| - if (duplicate_class_id >= 0) {
|
| - // This check is valid only when checking the receiver.
|
| - ASSERT((arg_nr != 0) ||
|
| - (result.GetTargetAt(duplicate_class_id) == GetTargetAt(i)));
|
| - result.IncrementCountAt(duplicate_class_id, count);
|
| - } else {
|
| - // This will make sure that Smi is first if it exists.
|
| - result.AddReceiverCheck(class_id,
|
| - Function::Handle(GetTargetAt(i)),
|
| - count);
|
| - }
|
| - }
|
| - // Copy deoptimization reason.
|
| - result.set_deopt_reason(deopt_reason());
|
| -
|
| - return result.raw();
|
| -}
|
| -
|
| -
|
| -bool ICData::AllTargetsHaveSameOwner(intptr_t owner_cid) const {
|
| - if (NumberOfChecks() == 0) return false;
|
| - Class& cls = Class::Handle();
|
| - const intptr_t len = NumberOfChecks();
|
| - for (intptr_t i = 0; i < len; i++) {
|
| - cls = Function::Handle(GetTargetAt(i)).Owner();
|
| - if (cls.id() != owner_cid) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -
|
| -bool ICData::AllReceiversAreNumbers() const {
|
| - if (NumberOfChecks() == 0) return false;
|
| - Class& cls = Class::Handle();
|
| - const intptr_t len = NumberOfChecks();
|
| - for (intptr_t i = 0; i < len; i++) {
|
| - cls = Function::Handle(GetTargetAt(i)).Owner();
|
| - const intptr_t cid = cls.id();
|
| - if ((cid != kSmiCid) &&
|
| - (cid != kMintCid) &&
|
| - (cid != kBigintCid) &&
|
| - (cid != kDoubleCid)) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -
|
| -bool ICData::HasReceiverClassId(intptr_t class_id) const {
|
| - ASSERT(num_args_tested() > 0);
|
| - const intptr_t len = NumberOfChecks();
|
| - for (intptr_t i = 0; i < len; i++) {
|
| - const intptr_t test_class_id = GetReceiverClassIdAt(i);
|
| - if (test_class_id == class_id) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -
|
| -// Returns true if all targets are the same.
|
| -// TODO(srdjan): if targets are native use their C_function to compare.
|
| -bool ICData::HasOneTarget() const {
|
| - ASSERT(NumberOfChecks() > 0);
|
| - const Function& first_target = Function::Handle(GetTargetAt(0));
|
| - const intptr_t len = NumberOfChecks();
|
| - for (intptr_t i = 1; i < len; i++) {
|
| - if (GetTargetAt(i) != first_target.raw()) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -
|
| -RawICData* ICData::New(const Function& caller_function,
|
| - const String& target_name,
|
| - const Array& arguments_descriptor,
|
| - intptr_t deopt_id,
|
| - intptr_t num_args_tested) {
|
| - ASSERT(!caller_function.IsNull());
|
| - ASSERT(!target_name.IsNull());
|
| - ASSERT(!arguments_descriptor.IsNull());
|
| - ASSERT(Object::icdata_class() != Class::null());
|
| - ASSERT(num_args_tested >= 0);
|
| - ICData& result = ICData::Handle();
|
| - {
|
| - // IC data objects are long living objects, allocate them in old generation.
|
| - RawObject* raw = Object::Allocate(ICData::kClassId,
|
| - ICData::InstanceSize(),
|
| - Heap::kOld);
|
| - NoGCScope no_gc;
|
| - result ^= raw;
|
| - }
|
| - result.set_function(caller_function);
|
| - result.set_target_name(target_name);
|
| - result.set_arguments_descriptor(arguments_descriptor);
|
| - result.set_deopt_id(deopt_id);
|
| - result.set_num_args_tested(num_args_tested);
|
| - result.set_deopt_reason(kDeoptUnknown);
|
| - result.set_is_closure_call(false);
|
| - // Number of array elements in one test entry.
|
| - intptr_t len = result.TestEntryLength();
|
| - // IC data array must be null terminated (sentinel entry).
|
| - const Array& ic_data = Array::Handle(Array::New(len, Heap::kOld));
|
| - result.set_ic_data(ic_data);
|
| - result.WriteSentinel(ic_data);
|
| - return result.raw();
|
| -}
|
| -
|
| -
|
| -void ICData::PrintToJSONStream(JSONStream* stream, bool ref) const {
|
| - Object::PrintToJSONStream(stream, ref);
|
| -}
|
| -
|
| -
|
| RawArray* MegamorphicCache::buckets() const {
|
| return raw_ptr()->buckets_;
|
| }
|
|
|