1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/type-info.h"
6
7 #include "src/ast/ast.h"
8 #include "src/code-stubs.h"
9 #include "src/compiler.h"
10 #include "src/ic/ic.h"
11 #include "src/ic/stub-cache.h"
12 #include "src/objects-inl.h"
13
14 namespace v8 {
15 namespace internal {
16
17
TypeFeedbackOracle(Isolate * isolate,Zone * zone,Handle<Code> code,Handle<TypeFeedbackVector> feedback_vector,Handle<Context> native_context)18 TypeFeedbackOracle::TypeFeedbackOracle(
19 Isolate* isolate, Zone* zone, Handle<Code> code,
20 Handle<TypeFeedbackVector> feedback_vector, Handle<Context> native_context)
21 : native_context_(native_context), isolate_(isolate), zone_(zone) {
22 BuildDictionary(code);
23 DCHECK(dictionary_->IsDictionary());
24 // We make a copy of the feedback vector because a GC could clear
25 // the type feedback info contained therein.
26 // TODO(mvstanton): revisit the decision to copy when we weakly
27 // traverse the feedback vector at GC time.
28 feedback_vector_ = TypeFeedbackVector::Copy(isolate, feedback_vector);
29 }
30
31
IdToKey(TypeFeedbackId ast_id)32 static uint32_t IdToKey(TypeFeedbackId ast_id) {
33 return static_cast<uint32_t>(ast_id.ToInt());
34 }
35
36
GetInfo(TypeFeedbackId ast_id)37 Handle<Object> TypeFeedbackOracle::GetInfo(TypeFeedbackId ast_id) {
38 int entry = dictionary_->FindEntry(IdToKey(ast_id));
39 if (entry != UnseededNumberDictionary::kNotFound) {
40 Object* value = dictionary_->ValueAt(entry);
41 if (value->IsCell()) {
42 Cell* cell = Cell::cast(value);
43 return Handle<Object>(cell->value(), isolate());
44 } else {
45 return Handle<Object>(value, isolate());
46 }
47 }
48 return Handle<Object>::cast(isolate()->factory()->undefined_value());
49 }
50
51
GetInfo(FeedbackVectorSlot slot)52 Handle<Object> TypeFeedbackOracle::GetInfo(FeedbackVectorSlot slot) {
53 DCHECK(slot.ToInt() >= 0 && slot.ToInt() < feedback_vector_->length());
54 Handle<Object> undefined =
55 Handle<Object>::cast(isolate()->factory()->undefined_value());
56 Object* obj = feedback_vector_->Get(slot);
57
58 // Slots do not embed direct pointers to maps, functions. Instead
59 // a WeakCell is always used.
60 if (obj->IsWeakCell()) {
61 WeakCell* cell = WeakCell::cast(obj);
62 if (cell->cleared()) return undefined;
63 obj = cell->value();
64 }
65
66 if (obj->IsJSFunction() || obj->IsAllocationSite() || obj->IsSymbol() ||
67 obj->IsSimd128Value()) {
68 return Handle<Object>(obj, isolate());
69 }
70
71 return undefined;
72 }
73
74
LoadInlineCacheState(FeedbackVectorSlot slot)75 InlineCacheState TypeFeedbackOracle::LoadInlineCacheState(
76 FeedbackVectorSlot slot) {
77 if (!slot.IsInvalid()) {
78 FeedbackVectorSlotKind kind = feedback_vector_->GetKind(slot);
79 if (kind == FeedbackVectorSlotKind::LOAD_IC) {
80 LoadICNexus nexus(feedback_vector_, slot);
81 return nexus.StateFromFeedback();
82 } else if (kind == FeedbackVectorSlotKind::KEYED_LOAD_IC) {
83 KeyedLoadICNexus nexus(feedback_vector_, slot);
84 return nexus.StateFromFeedback();
85 }
86 }
87
88 // If we can't find an IC, assume we've seen *something*, but we don't know
89 // what. PREMONOMORPHIC roughly encodes this meaning.
90 return PREMONOMORPHIC;
91 }
92
93
StoreIsUninitialized(FeedbackVectorSlot slot)94 bool TypeFeedbackOracle::StoreIsUninitialized(FeedbackVectorSlot slot) {
95 if (!slot.IsInvalid()) {
96 FeedbackVectorSlotKind kind = feedback_vector_->GetKind(slot);
97 if (kind == FeedbackVectorSlotKind::STORE_IC) {
98 StoreICNexus nexus(feedback_vector_, slot);
99 return nexus.StateFromFeedback() == UNINITIALIZED;
100 } else if (kind == FeedbackVectorSlotKind::KEYED_STORE_IC) {
101 KeyedStoreICNexus nexus(feedback_vector_, slot);
102 return nexus.StateFromFeedback() == UNINITIALIZED;
103 }
104 }
105 return true;
106 }
107
108
CallIsUninitialized(FeedbackVectorSlot slot)109 bool TypeFeedbackOracle::CallIsUninitialized(FeedbackVectorSlot slot) {
110 Handle<Object> value = GetInfo(slot);
111 return value->IsUndefined() ||
112 value.is_identical_to(
113 TypeFeedbackVector::UninitializedSentinel(isolate()));
114 }
115
116
CallIsMonomorphic(FeedbackVectorSlot slot)117 bool TypeFeedbackOracle::CallIsMonomorphic(FeedbackVectorSlot slot) {
118 Handle<Object> value = GetInfo(slot);
119 return value->IsAllocationSite() || value->IsJSFunction();
120 }
121
122
CallNewIsMonomorphic(FeedbackVectorSlot slot)123 bool TypeFeedbackOracle::CallNewIsMonomorphic(FeedbackVectorSlot slot) {
124 Handle<Object> info = GetInfo(slot);
125 return info->IsAllocationSite() || info->IsJSFunction();
126 }
127
128
ForInType(FeedbackVectorSlot feedback_vector_slot)129 byte TypeFeedbackOracle::ForInType(FeedbackVectorSlot feedback_vector_slot) {
130 Handle<Object> value = GetInfo(feedback_vector_slot);
131 return value.is_identical_to(
132 TypeFeedbackVector::UninitializedSentinel(isolate()))
133 ? ForInStatement::FAST_FOR_IN
134 : ForInStatement::SLOW_FOR_IN;
135 }
136
137
GetStoreModeAndKeyType(FeedbackVectorSlot slot,KeyedAccessStoreMode * store_mode,IcCheckType * key_type)138 void TypeFeedbackOracle::GetStoreModeAndKeyType(
139 FeedbackVectorSlot slot, KeyedAccessStoreMode* store_mode,
140 IcCheckType* key_type) {
141 if (!slot.IsInvalid() &&
142 feedback_vector_->GetKind(slot) ==
143 FeedbackVectorSlotKind::KEYED_STORE_IC) {
144 KeyedStoreICNexus nexus(feedback_vector_, slot);
145 *store_mode = nexus.GetKeyedAccessStoreMode();
146 *key_type = nexus.GetKeyType();
147 } else {
148 *store_mode = STANDARD_STORE;
149 *key_type = ELEMENT;
150 }
151 }
152
153
GetCallTarget(FeedbackVectorSlot slot)154 Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(FeedbackVectorSlot slot) {
155 Handle<Object> info = GetInfo(slot);
156 if (info->IsAllocationSite()) {
157 return Handle<JSFunction>(isolate()->native_context()->array_function());
158 }
159
160 return Handle<JSFunction>::cast(info);
161 }
162
163
GetCallNewTarget(FeedbackVectorSlot slot)164 Handle<JSFunction> TypeFeedbackOracle::GetCallNewTarget(
165 FeedbackVectorSlot slot) {
166 Handle<Object> info = GetInfo(slot);
167 if (info->IsJSFunction()) {
168 return Handle<JSFunction>::cast(info);
169 }
170
171 DCHECK(info->IsAllocationSite());
172 return Handle<JSFunction>(isolate()->native_context()->array_function());
173 }
174
175
GetCallAllocationSite(FeedbackVectorSlot slot)176 Handle<AllocationSite> TypeFeedbackOracle::GetCallAllocationSite(
177 FeedbackVectorSlot slot) {
178 Handle<Object> info = GetInfo(slot);
179 if (info->IsAllocationSite()) {
180 return Handle<AllocationSite>::cast(info);
181 }
182 return Handle<AllocationSite>::null();
183 }
184
185
GetCallNewAllocationSite(FeedbackVectorSlot slot)186 Handle<AllocationSite> TypeFeedbackOracle::GetCallNewAllocationSite(
187 FeedbackVectorSlot slot) {
188 Handle<Object> info = GetInfo(slot);
189 if (info->IsAllocationSite()) {
190 return Handle<AllocationSite>::cast(info);
191 }
192 return Handle<AllocationSite>::null();
193 }
194
195
CompareType(TypeFeedbackId id,Type ** left_type,Type ** right_type,Type ** combined_type)196 void TypeFeedbackOracle::CompareType(TypeFeedbackId id,
197 Type** left_type,
198 Type** right_type,
199 Type** combined_type) {
200 Handle<Object> info = GetInfo(id);
201 if (!info->IsCode()) {
202 // For some comparisons we don't have ICs, e.g. LiteralCompareTypeof.
203 *left_type = *right_type = *combined_type = Type::None(zone());
204 return;
205 }
206 Handle<Code> code = Handle<Code>::cast(info);
207
208 Handle<Map> map;
209 Map* raw_map = code->FindFirstMap();
210 if (raw_map != NULL) Map::TryUpdate(handle(raw_map)).ToHandle(&map);
211
212 if (code->is_compare_ic_stub()) {
213 CompareICStub stub(code->stub_key(), isolate());
214 *left_type = CompareICState::StateToType(zone(), stub.left());
215 *right_type = CompareICState::StateToType(zone(), stub.right());
216 *combined_type = CompareICState::StateToType(zone(), stub.state(), map);
217 } else if (code->is_compare_nil_ic_stub()) {
218 CompareNilICStub stub(isolate(), code->extra_ic_state());
219 *combined_type = stub.GetType(zone(), map);
220 *left_type = *right_type = stub.GetInputType(zone(), map);
221 }
222 }
223
224
BinaryType(TypeFeedbackId id,Type ** left,Type ** right,Type ** result,Maybe<int> * fixed_right_arg,Handle<AllocationSite> * allocation_site,Token::Value op)225 void TypeFeedbackOracle::BinaryType(TypeFeedbackId id,
226 Type** left,
227 Type** right,
228 Type** result,
229 Maybe<int>* fixed_right_arg,
230 Handle<AllocationSite>* allocation_site,
231 Token::Value op) {
232 Handle<Object> object = GetInfo(id);
233 if (!object->IsCode()) {
234 // For some binary ops we don't have ICs, e.g. Token::COMMA, but for the
235 // operations covered by the BinaryOpIC we should always have them.
236 DCHECK(op < BinaryOpICState::FIRST_TOKEN ||
237 op > BinaryOpICState::LAST_TOKEN);
238 *left = *right = *result = Type::None(zone());
239 *fixed_right_arg = Nothing<int>();
240 *allocation_site = Handle<AllocationSite>::null();
241 return;
242 }
243 Handle<Code> code = Handle<Code>::cast(object);
244 DCHECK_EQ(Code::BINARY_OP_IC, code->kind());
245 BinaryOpICState state(isolate(), code->extra_ic_state());
246 DCHECK_EQ(op, state.op());
247
248 *left = state.GetLeftType();
249 *right = state.GetRightType();
250 *result = state.GetResultType();
251 *fixed_right_arg = state.fixed_right_arg();
252
253 AllocationSite* first_allocation_site = code->FindFirstAllocationSite();
254 if (first_allocation_site != NULL) {
255 *allocation_site = handle(first_allocation_site);
256 } else {
257 *allocation_site = Handle<AllocationSite>::null();
258 }
259 }
260
261
CountType(TypeFeedbackId id)262 Type* TypeFeedbackOracle::CountType(TypeFeedbackId id) {
263 Handle<Object> object = GetInfo(id);
264 if (!object->IsCode()) return Type::None(zone());
265 Handle<Code> code = Handle<Code>::cast(object);
266 DCHECK_EQ(Code::BINARY_OP_IC, code->kind());
267 BinaryOpICState state(isolate(), code->extra_ic_state());
268 return state.GetLeftType();
269 }
270
271
HasOnlyStringMaps(SmallMapList * receiver_types)272 bool TypeFeedbackOracle::HasOnlyStringMaps(SmallMapList* receiver_types) {
273 bool all_strings = receiver_types->length() > 0;
274 for (int i = 0; i < receiver_types->length(); i++) {
275 all_strings &= receiver_types->at(i)->IsStringMap();
276 }
277 return all_strings;
278 }
279
280
PropertyReceiverTypes(FeedbackVectorSlot slot,Handle<Name> name,SmallMapList * receiver_types)281 void TypeFeedbackOracle::PropertyReceiverTypes(FeedbackVectorSlot slot,
282 Handle<Name> name,
283 SmallMapList* receiver_types) {
284 receiver_types->Clear();
285 if (!slot.IsInvalid()) {
286 LoadICNexus nexus(feedback_vector_, slot);
287 Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC);
288 CollectReceiverTypes(&nexus, name, flags, receiver_types);
289 }
290 }
291
292
KeyedPropertyReceiverTypes(FeedbackVectorSlot slot,SmallMapList * receiver_types,bool * is_string,IcCheckType * key_type)293 void TypeFeedbackOracle::KeyedPropertyReceiverTypes(
294 FeedbackVectorSlot slot, SmallMapList* receiver_types, bool* is_string,
295 IcCheckType* key_type) {
296 receiver_types->Clear();
297 if (slot.IsInvalid()) {
298 *is_string = false;
299 *key_type = ELEMENT;
300 } else {
301 KeyedLoadICNexus nexus(feedback_vector_, slot);
302 CollectReceiverTypes<FeedbackNexus>(&nexus, receiver_types);
303 *is_string = HasOnlyStringMaps(receiver_types);
304 *key_type = nexus.FindFirstName() != NULL ? PROPERTY : ELEMENT;
305 }
306 }
307
308
AssignmentReceiverTypes(FeedbackVectorSlot slot,Handle<Name> name,SmallMapList * receiver_types)309 void TypeFeedbackOracle::AssignmentReceiverTypes(FeedbackVectorSlot slot,
310 Handle<Name> name,
311 SmallMapList* receiver_types) {
312 receiver_types->Clear();
313 Code::Flags flags = Code::ComputeHandlerFlags(Code::STORE_IC);
314 CollectReceiverTypes(slot, name, flags, receiver_types);
315 }
316
317
KeyedAssignmentReceiverTypes(FeedbackVectorSlot slot,SmallMapList * receiver_types,KeyedAccessStoreMode * store_mode,IcCheckType * key_type)318 void TypeFeedbackOracle::KeyedAssignmentReceiverTypes(
319 FeedbackVectorSlot slot, SmallMapList* receiver_types,
320 KeyedAccessStoreMode* store_mode, IcCheckType* key_type) {
321 receiver_types->Clear();
322 CollectReceiverTypes(slot, receiver_types);
323 GetStoreModeAndKeyType(slot, store_mode, key_type);
324 }
325
326
CountReceiverTypes(FeedbackVectorSlot slot,SmallMapList * receiver_types)327 void TypeFeedbackOracle::CountReceiverTypes(FeedbackVectorSlot slot,
328 SmallMapList* receiver_types) {
329 receiver_types->Clear();
330 if (!slot.IsInvalid()) CollectReceiverTypes(slot, receiver_types);
331 }
332
333
CollectReceiverTypes(FeedbackVectorSlot slot,Handle<Name> name,Code::Flags flags,SmallMapList * types)334 void TypeFeedbackOracle::CollectReceiverTypes(FeedbackVectorSlot slot,
335 Handle<Name> name,
336 Code::Flags flags,
337 SmallMapList* types) {
338 StoreICNexus nexus(feedback_vector_, slot);
339 CollectReceiverTypes<FeedbackNexus>(&nexus, name, flags, types);
340 }
341
342
343 template <class T>
CollectReceiverTypes(T * obj,Handle<Name> name,Code::Flags flags,SmallMapList * types)344 void TypeFeedbackOracle::CollectReceiverTypes(T* obj, Handle<Name> name,
345 Code::Flags flags,
346 SmallMapList* types) {
347 if (FLAG_collect_megamorphic_maps_from_stub_cache &&
348 obj->ic_state() == MEGAMORPHIC) {
349 types->Reserve(4, zone());
350 isolate()->stub_cache()->CollectMatchingMaps(
351 types, name, flags, native_context_, zone());
352 } else {
353 CollectReceiverTypes<T>(obj, types);
354 }
355 }
356
357
CollectReceiverTypes(FeedbackVectorSlot slot,SmallMapList * types)358 void TypeFeedbackOracle::CollectReceiverTypes(FeedbackVectorSlot slot,
359 SmallMapList* types) {
360 FeedbackVectorSlotKind kind = feedback_vector_->GetKind(slot);
361 if (kind == FeedbackVectorSlotKind::STORE_IC) {
362 StoreICNexus nexus(feedback_vector_, slot);
363 CollectReceiverTypes<FeedbackNexus>(&nexus, types);
364 } else {
365 DCHECK_EQ(FeedbackVectorSlotKind::KEYED_STORE_IC, kind);
366 KeyedStoreICNexus nexus(feedback_vector_, slot);
367 CollectReceiverTypes<FeedbackNexus>(&nexus, types);
368 }
369 }
370
371
372 template <class T>
CollectReceiverTypes(T * obj,SmallMapList * types)373 void TypeFeedbackOracle::CollectReceiverTypes(T* obj, SmallMapList* types) {
374 MapHandleList maps;
375 if (obj->ic_state() == MONOMORPHIC) {
376 Map* map = obj->FindFirstMap();
377 if (map != NULL) maps.Add(handle(map));
378 } else if (obj->ic_state() == POLYMORPHIC) {
379 obj->FindAllMaps(&maps);
380 } else {
381 return;
382 }
383 types->Reserve(maps.length(), zone());
384 for (int i = 0; i < maps.length(); i++) {
385 Handle<Map> map(maps.at(i));
386 if (IsRelevantFeedback(*map, *native_context_)) {
387 types->AddMapIfMissing(maps.at(i), zone());
388 }
389 }
390 }
391
392
ToBooleanTypes(TypeFeedbackId id)393 uint16_t TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId id) {
394 Handle<Object> object = GetInfo(id);
395 return object->IsCode() ? Handle<Code>::cast(object)->to_boolean_state() : 0;
396 }
397
398
399 // Things are a bit tricky here: The iterator for the RelocInfos and the infos
400 // themselves are not GC-safe, so we first get all infos, then we create the
401 // dictionary (possibly triggering GC), and finally we relocate the collected
402 // infos before we process them.
BuildDictionary(Handle<Code> code)403 void TypeFeedbackOracle::BuildDictionary(Handle<Code> code) {
404 DisallowHeapAllocation no_allocation;
405 ZoneList<RelocInfo> infos(16, zone());
406 HandleScope scope(isolate());
407 GetRelocInfos(code, &infos);
408 CreateDictionary(code, &infos);
409 ProcessRelocInfos(&infos);
410 // Allocate handle in the parent scope.
411 dictionary_ = scope.CloseAndEscape(dictionary_);
412 }
413
414
GetRelocInfos(Handle<Code> code,ZoneList<RelocInfo> * infos)415 void TypeFeedbackOracle::GetRelocInfos(Handle<Code> code,
416 ZoneList<RelocInfo>* infos) {
417 int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID);
418 for (RelocIterator it(*code, mask); !it.done(); it.next()) {
419 infos->Add(*it.rinfo(), zone());
420 }
421 }
422
423
CreateDictionary(Handle<Code> code,ZoneList<RelocInfo> * infos)424 void TypeFeedbackOracle::CreateDictionary(Handle<Code> code,
425 ZoneList<RelocInfo>* infos) {
426 AllowHeapAllocation allocation_allowed;
427 Code* old_code = *code;
428 dictionary_ = UnseededNumberDictionary::New(isolate(), infos->length());
429 RelocateRelocInfos(infos, old_code, *code);
430 }
431
432
RelocateRelocInfos(ZoneList<RelocInfo> * infos,Code * old_code,Code * new_code)433 void TypeFeedbackOracle::RelocateRelocInfos(ZoneList<RelocInfo>* infos,
434 Code* old_code,
435 Code* new_code) {
436 for (int i = 0; i < infos->length(); i++) {
437 RelocInfo* info = &(*infos)[i];
438 info->set_host(new_code);
439 info->set_pc(new_code->instruction_start() +
440 (info->pc() - old_code->instruction_start()));
441 }
442 }
443
444
ProcessRelocInfos(ZoneList<RelocInfo> * infos)445 void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) {
446 for (int i = 0; i < infos->length(); i++) {
447 RelocInfo reloc_entry = (*infos)[i];
448 Address target_address = reloc_entry.target_address();
449 TypeFeedbackId ast_id =
450 TypeFeedbackId(static_cast<unsigned>((*infos)[i].data()));
451 Code* target = Code::GetCodeFromTargetAddress(target_address);
452 switch (target->kind()) {
453 case Code::LOAD_IC:
454 case Code::STORE_IC:
455 case Code::KEYED_LOAD_IC:
456 case Code::KEYED_STORE_IC:
457 case Code::BINARY_OP_IC:
458 case Code::COMPARE_IC:
459 case Code::TO_BOOLEAN_IC:
460 case Code::COMPARE_NIL_IC:
461 SetInfo(ast_id, target);
462 break;
463
464 default:
465 break;
466 }
467 }
468 }
469
470
SetInfo(TypeFeedbackId ast_id,Object * target)471 void TypeFeedbackOracle::SetInfo(TypeFeedbackId ast_id, Object* target) {
472 DCHECK(dictionary_->FindEntry(IdToKey(ast_id)) ==
473 UnseededNumberDictionary::kNotFound);
474 // Dictionary has been allocated with sufficient size for all elements.
475 DisallowHeapAllocation no_need_to_resize_dictionary;
476 HandleScope scope(isolate());
477 USE(UnseededNumberDictionary::AtNumberPut(
478 dictionary_, IdToKey(ast_id), handle(target, isolate())));
479 }
480
481
482 } // namespace internal
483 } // namespace v8
484