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