• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #include "v8.h"
29 
30 #include "ast.h"
31 #include "code-stubs.h"
32 #include "compiler.h"
33 #include "ic.h"
34 #include "macro-assembler.h"
35 #include "stub-cache.h"
36 #include "type-info.h"
37 
38 #include "ic-inl.h"
39 #include "objects-inl.h"
40 
41 namespace v8 {
42 namespace internal {
43 
44 
TypeFromValue(Handle<Object> value)45 TypeInfo TypeInfo::TypeFromValue(Handle<Object> value) {
46   TypeInfo info;
47   if (value->IsSmi()) {
48     info = TypeInfo::Smi();
49   } else if (value->IsHeapNumber()) {
50     info = TypeInfo::IsInt32Double(HeapNumber::cast(*value)->value())
51         ? TypeInfo::Integer32()
52         : TypeInfo::Double();
53   } else if (value->IsString()) {
54     info = TypeInfo::String();
55   } else {
56     info = TypeInfo::Unknown();
57   }
58   return info;
59 }
60 
61 
TypeFeedbackOracle(Handle<Code> code,Handle<Context> global_context,Isolate * isolate)62 TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code,
63                                        Handle<Context> global_context,
64                                        Isolate* isolate) {
65   global_context_ = global_context;
66   isolate_ = isolate;
67   BuildDictionary(code);
68   ASSERT(reinterpret_cast<Address>(*dictionary_.location()) != kHandleZapValue);
69 }
70 
71 
GetInfo(unsigned ast_id)72 Handle<Object> TypeFeedbackOracle::GetInfo(unsigned ast_id) {
73   int entry = dictionary_->FindEntry(ast_id);
74   return entry != UnseededNumberDictionary::kNotFound
75       ? Handle<Object>(dictionary_->ValueAt(entry))
76       : Handle<Object>::cast(isolate_->factory()->undefined_value());
77 }
78 
79 
LoadIsUninitialized(Property * expr)80 bool TypeFeedbackOracle::LoadIsUninitialized(Property* expr) {
81   Handle<Object> map_or_code = GetInfo(expr->id());
82   if (map_or_code->IsMap()) return false;
83   if (map_or_code->IsCode()) {
84     Handle<Code> code = Handle<Code>::cast(map_or_code);
85     return code->is_inline_cache_stub() && code->ic_state() == UNINITIALIZED;
86   }
87   return false;
88 }
89 
90 
LoadIsMonomorphicNormal(Property * expr)91 bool TypeFeedbackOracle::LoadIsMonomorphicNormal(Property* expr) {
92   Handle<Object> map_or_code = GetInfo(expr->id());
93   if (map_or_code->IsMap()) return true;
94   if (map_or_code->IsCode()) {
95     Handle<Code> code = Handle<Code>::cast(map_or_code);
96     return code->is_keyed_load_stub() &&
97         code->ic_state() == MONOMORPHIC &&
98         Code::ExtractTypeFromFlags(code->flags()) == NORMAL &&
99         code->FindFirstMap() != NULL &&
100         !CanRetainOtherContext(code->FindFirstMap(), *global_context_);
101   }
102   return false;
103 }
104 
105 
LoadIsMegamorphicWithTypeInfo(Property * expr)106 bool TypeFeedbackOracle::LoadIsMegamorphicWithTypeInfo(Property* expr) {
107   Handle<Object> map_or_code = GetInfo(expr->id());
108   if (map_or_code->IsCode()) {
109     Handle<Code> code = Handle<Code>::cast(map_or_code);
110     Builtins* builtins = isolate_->builtins();
111     return code->is_keyed_load_stub() &&
112         *code != builtins->builtin(Builtins::kKeyedLoadIC_Generic) &&
113         code->ic_state() == MEGAMORPHIC;
114   }
115   return false;
116 }
117 
118 
StoreIsMonomorphicNormal(Expression * expr)119 bool TypeFeedbackOracle::StoreIsMonomorphicNormal(Expression* expr) {
120   Handle<Object> map_or_code = GetInfo(expr->id());
121   if (map_or_code->IsMap()) return true;
122   if (map_or_code->IsCode()) {
123     Handle<Code> code = Handle<Code>::cast(map_or_code);
124     bool allow_growth =
125         Code::GetKeyedAccessGrowMode(code->extra_ic_state()) ==
126         ALLOW_JSARRAY_GROWTH;
127     return code->is_keyed_store_stub() &&
128         !allow_growth &&
129         code->ic_state() == MONOMORPHIC &&
130         Code::ExtractTypeFromFlags(code->flags()) == NORMAL &&
131         code->FindFirstMap() != NULL &&
132         !CanRetainOtherContext(code->FindFirstMap(), *global_context_);
133   }
134   return false;
135 }
136 
137 
StoreIsMegamorphicWithTypeInfo(Expression * expr)138 bool TypeFeedbackOracle::StoreIsMegamorphicWithTypeInfo(Expression* expr) {
139   Handle<Object> map_or_code = GetInfo(expr->id());
140   if (map_or_code->IsCode()) {
141     Handle<Code> code = Handle<Code>::cast(map_or_code);
142     Builtins* builtins = isolate_->builtins();
143     bool allow_growth =
144         Code::GetKeyedAccessGrowMode(code->extra_ic_state()) ==
145         ALLOW_JSARRAY_GROWTH;
146     return code->is_keyed_store_stub() &&
147         !allow_growth &&
148         *code != builtins->builtin(Builtins::kKeyedStoreIC_Generic) &&
149         *code != builtins->builtin(Builtins::kKeyedStoreIC_Generic_Strict) &&
150         code->ic_state() == MEGAMORPHIC;
151   }
152   return false;
153 }
154 
155 
CallIsMonomorphic(Call * expr)156 bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) {
157   Handle<Object> value = GetInfo(expr->id());
158   return value->IsMap() || value->IsSmi() || value->IsJSFunction();
159 }
160 
161 
CallNewIsMonomorphic(CallNew * expr)162 bool TypeFeedbackOracle::CallNewIsMonomorphic(CallNew* expr) {
163   Handle<Object> value = GetInfo(expr->id());
164   return value->IsJSFunction();
165 }
166 
167 
ObjectLiteralStoreIsMonomorphic(ObjectLiteral::Property * prop)168 bool TypeFeedbackOracle::ObjectLiteralStoreIsMonomorphic(
169     ObjectLiteral::Property* prop) {
170   Handle<Object> map_or_code = GetInfo(prop->key()->id());
171   return map_or_code->IsMap();
172 }
173 
174 
IsForInFastCase(ForInStatement * stmt)175 bool TypeFeedbackOracle::IsForInFastCase(ForInStatement* stmt) {
176   Handle<Object> value = GetInfo(stmt->PrepareId());
177   return value->IsSmi() &&
178       Smi::cast(*value)->value() == TypeFeedbackCells::kForInFastCaseMarker;
179 }
180 
181 
LoadMonomorphicReceiverType(Property * expr)182 Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) {
183   ASSERT(LoadIsMonomorphicNormal(expr));
184   Handle<Object> map_or_code = GetInfo(expr->id());
185   if (map_or_code->IsCode()) {
186     Handle<Code> code = Handle<Code>::cast(map_or_code);
187     Map* first_map = code->FindFirstMap();
188     ASSERT(first_map != NULL);
189     return CanRetainOtherContext(first_map, *global_context_)
190         ? Handle<Map>::null()
191         : Handle<Map>(first_map);
192   }
193   return Handle<Map>::cast(map_or_code);
194 }
195 
196 
StoreMonomorphicReceiverType(Expression * expr)197 Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Expression* expr) {
198   ASSERT(StoreIsMonomorphicNormal(expr));
199   Handle<Object> map_or_code = GetInfo(expr->id());
200   if (map_or_code->IsCode()) {
201     Handle<Code> code = Handle<Code>::cast(map_or_code);
202     Map* first_map = code->FindFirstMap();
203     ASSERT(first_map != NULL);
204     return CanRetainOtherContext(first_map, *global_context_)
205         ? Handle<Map>::null()
206         : Handle<Map>(first_map);
207   }
208   return Handle<Map>::cast(map_or_code);
209 }
210 
211 
LoadReceiverTypes(Property * expr,Handle<String> name,SmallMapList * types)212 void TypeFeedbackOracle::LoadReceiverTypes(Property* expr,
213                                            Handle<String> name,
214                                            SmallMapList* types) {
215   Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL);
216   CollectReceiverTypes(expr->id(), name, flags, types);
217 }
218 
219 
StoreReceiverTypes(Assignment * expr,Handle<String> name,SmallMapList * types)220 void TypeFeedbackOracle::StoreReceiverTypes(Assignment* expr,
221                                             Handle<String> name,
222                                             SmallMapList* types) {
223   Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, NORMAL);
224   CollectReceiverTypes(expr->id(), name, flags, types);
225 }
226 
227 
CallReceiverTypes(Call * expr,Handle<String> name,CallKind call_kind,SmallMapList * types)228 void TypeFeedbackOracle::CallReceiverTypes(Call* expr,
229                                            Handle<String> name,
230                                            CallKind call_kind,
231                                            SmallMapList* types) {
232   int arity = expr->arguments()->length();
233 
234   // Note: Currently we do not take string extra ic data into account
235   // here.
236   Code::ExtraICState extra_ic_state =
237       CallIC::Contextual::encode(call_kind == CALL_AS_FUNCTION);
238 
239   Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
240                                                     NORMAL,
241                                                     extra_ic_state,
242                                                     OWN_MAP,
243                                                     arity);
244   CollectReceiverTypes(expr->id(), name, flags, types);
245 }
246 
247 
GetCallCheckType(Call * expr)248 CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) {
249   Handle<Object> value = GetInfo(expr->id());
250   if (!value->IsSmi()) return RECEIVER_MAP_CHECK;
251   CheckType check = static_cast<CheckType>(Smi::cast(*value)->value());
252   ASSERT(check != RECEIVER_MAP_CHECK);
253   return check;
254 }
255 
256 
GetPrototypeForPrimitiveCheck(CheckType check)257 Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
258     CheckType check) {
259   JSFunction* function = NULL;
260   switch (check) {
261     case RECEIVER_MAP_CHECK:
262       UNREACHABLE();
263       break;
264     case STRING_CHECK:
265       function = global_context_->string_function();
266       break;
267     case NUMBER_CHECK:
268       function = global_context_->number_function();
269       break;
270     case BOOLEAN_CHECK:
271       function = global_context_->boolean_function();
272       break;
273   }
274   ASSERT(function != NULL);
275   return Handle<JSObject>(JSObject::cast(function->instance_prototype()));
276 }
277 
278 
GetCallTarget(Call * expr)279 Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(Call* expr) {
280   return Handle<JSFunction>::cast(GetInfo(expr->id()));
281 }
282 
283 
GetCallNewTarget(CallNew * expr)284 Handle<JSFunction> TypeFeedbackOracle::GetCallNewTarget(CallNew* expr) {
285   return Handle<JSFunction>::cast(GetInfo(expr->id()));
286 }
287 
288 
GetObjectLiteralStoreMap(ObjectLiteral::Property * prop)289 Handle<Map> TypeFeedbackOracle::GetObjectLiteralStoreMap(
290     ObjectLiteral::Property* prop) {
291   ASSERT(ObjectLiteralStoreIsMonomorphic(prop));
292   return Handle<Map>::cast(GetInfo(prop->key()->id()));
293 }
294 
295 
LoadIsBuiltin(Property * expr,Builtins::Name id)296 bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) {
297   return *GetInfo(expr->id()) ==
298       isolate_->builtins()->builtin(id);
299 }
300 
301 
CompareType(CompareOperation * expr)302 TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) {
303   Handle<Object> object = GetInfo(expr->id());
304   TypeInfo unknown = TypeInfo::Unknown();
305   if (!object->IsCode()) return unknown;
306   Handle<Code> code = Handle<Code>::cast(object);
307   if (!code->is_compare_ic_stub()) return unknown;
308 
309   CompareIC::State state = static_cast<CompareIC::State>(code->compare_state());
310   switch (state) {
311     case CompareIC::UNINITIALIZED:
312       // Uninitialized means never executed.
313       return TypeInfo::Uninitialized();
314     case CompareIC::SMIS:
315       return TypeInfo::Smi();
316     case CompareIC::HEAP_NUMBERS:
317       return TypeInfo::Number();
318     case CompareIC::SYMBOLS:
319     case CompareIC::STRINGS:
320       return TypeInfo::String();
321     case CompareIC::OBJECTS:
322     case CompareIC::KNOWN_OBJECTS:
323       // TODO(kasperl): We really need a type for JS objects here.
324       return TypeInfo::NonPrimitive();
325     case CompareIC::GENERIC:
326     default:
327       return unknown;
328   }
329 }
330 
331 
IsSymbolCompare(CompareOperation * expr)332 bool TypeFeedbackOracle::IsSymbolCompare(CompareOperation* expr) {
333   Handle<Object> object = GetInfo(expr->id());
334   if (!object->IsCode()) return false;
335   Handle<Code> code = Handle<Code>::cast(object);
336   if (!code->is_compare_ic_stub()) return false;
337   CompareIC::State state = static_cast<CompareIC::State>(code->compare_state());
338   return state == CompareIC::SYMBOLS;
339 }
340 
341 
GetCompareMap(CompareOperation * expr)342 Handle<Map> TypeFeedbackOracle::GetCompareMap(CompareOperation* expr) {
343   Handle<Object> object = GetInfo(expr->id());
344   if (!object->IsCode()) return Handle<Map>::null();
345   Handle<Code> code = Handle<Code>::cast(object);
346   if (!code->is_compare_ic_stub()) return Handle<Map>::null();
347   CompareIC::State state = static_cast<CompareIC::State>(code->compare_state());
348   if (state != CompareIC::KNOWN_OBJECTS) {
349     return Handle<Map>::null();
350   }
351   Map* first_map = code->FindFirstMap();
352   ASSERT(first_map != NULL);
353   return CanRetainOtherContext(first_map, *global_context_)
354       ? Handle<Map>::null()
355       : Handle<Map>(first_map);
356 }
357 
358 
UnaryType(UnaryOperation * expr)359 TypeInfo TypeFeedbackOracle::UnaryType(UnaryOperation* expr) {
360   Handle<Object> object = GetInfo(expr->id());
361   TypeInfo unknown = TypeInfo::Unknown();
362   if (!object->IsCode()) return unknown;
363   Handle<Code> code = Handle<Code>::cast(object);
364   ASSERT(code->is_unary_op_stub());
365   UnaryOpIC::TypeInfo type = static_cast<UnaryOpIC::TypeInfo>(
366       code->unary_op_type());
367   switch (type) {
368     case UnaryOpIC::SMI:
369       return TypeInfo::Smi();
370     case UnaryOpIC::HEAP_NUMBER:
371       return TypeInfo::Double();
372     default:
373       return unknown;
374   }
375 }
376 
377 
BinaryType(BinaryOperation * expr)378 TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) {
379   Handle<Object> object = GetInfo(expr->id());
380   TypeInfo unknown = TypeInfo::Unknown();
381   if (!object->IsCode()) return unknown;
382   Handle<Code> code = Handle<Code>::cast(object);
383   if (code->is_binary_op_stub()) {
384     BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>(
385         code->binary_op_type());
386     BinaryOpIC::TypeInfo result_type = static_cast<BinaryOpIC::TypeInfo>(
387         code->binary_op_result_type());
388 
389     switch (type) {
390       case BinaryOpIC::UNINITIALIZED:
391         // Uninitialized means never executed.
392         return TypeInfo::Uninitialized();
393       case BinaryOpIC::SMI:
394         switch (result_type) {
395           case BinaryOpIC::UNINITIALIZED:
396             if (expr->op() == Token::DIV) {
397               return TypeInfo::Double();
398             }
399             return TypeInfo::Smi();
400           case BinaryOpIC::SMI:
401             return TypeInfo::Smi();
402           case BinaryOpIC::INT32:
403             return TypeInfo::Integer32();
404           case BinaryOpIC::HEAP_NUMBER:
405             return TypeInfo::Double();
406           default:
407             return unknown;
408         }
409       case BinaryOpIC::INT32:
410         if (expr->op() == Token::DIV ||
411             result_type == BinaryOpIC::HEAP_NUMBER) {
412           return TypeInfo::Double();
413         }
414         return TypeInfo::Integer32();
415       case BinaryOpIC::HEAP_NUMBER:
416         return TypeInfo::Double();
417       case BinaryOpIC::BOTH_STRING:
418         return TypeInfo::String();
419       case BinaryOpIC::STRING:
420       case BinaryOpIC::GENERIC:
421         return unknown;
422      default:
423         return unknown;
424     }
425   }
426   return unknown;
427 }
428 
429 
SwitchType(CaseClause * clause)430 TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) {
431   Handle<Object> object = GetInfo(clause->CompareId());
432   TypeInfo unknown = TypeInfo::Unknown();
433   if (!object->IsCode()) return unknown;
434   Handle<Code> code = Handle<Code>::cast(object);
435   if (!code->is_compare_ic_stub()) return unknown;
436 
437   CompareIC::State state = static_cast<CompareIC::State>(code->compare_state());
438   switch (state) {
439     case CompareIC::UNINITIALIZED:
440       // Uninitialized means never executed.
441       // TODO(fschneider): Introduce a separate value for never-executed ICs.
442       return unknown;
443     case CompareIC::SMIS:
444       return TypeInfo::Smi();
445     case CompareIC::STRINGS:
446       return TypeInfo::String();
447     case CompareIC::SYMBOLS:
448       return TypeInfo::Symbol();
449     case CompareIC::HEAP_NUMBERS:
450       return TypeInfo::Number();
451     case CompareIC::OBJECTS:
452     case CompareIC::KNOWN_OBJECTS:
453       // TODO(kasperl): We really need a type for JS objects here.
454       return TypeInfo::NonPrimitive();
455     case CompareIC::GENERIC:
456     default:
457       return unknown;
458   }
459 }
460 
461 
IncrementType(CountOperation * expr)462 TypeInfo TypeFeedbackOracle::IncrementType(CountOperation* expr) {
463   Handle<Object> object = GetInfo(expr->CountId());
464   TypeInfo unknown = TypeInfo::Unknown();
465   if (!object->IsCode()) return unknown;
466   Handle<Code> code = Handle<Code>::cast(object);
467   if (!code->is_binary_op_stub()) return unknown;
468 
469   BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>(
470       code->binary_op_type());
471   switch (type) {
472     case BinaryOpIC::UNINITIALIZED:
473     case BinaryOpIC::SMI:
474       return TypeInfo::Smi();
475     case BinaryOpIC::INT32:
476       return TypeInfo::Integer32();
477     case BinaryOpIC::HEAP_NUMBER:
478       return TypeInfo::Double();
479     case BinaryOpIC::BOTH_STRING:
480     case BinaryOpIC::STRING:
481     case BinaryOpIC::GENERIC:
482       return unknown;
483     default:
484       return unknown;
485   }
486   UNREACHABLE();
487   return unknown;
488 }
489 
490 
CollectReceiverTypes(unsigned ast_id,Handle<String> name,Code::Flags flags,SmallMapList * types)491 void TypeFeedbackOracle::CollectReceiverTypes(unsigned ast_id,
492                                               Handle<String> name,
493                                               Code::Flags flags,
494                                               SmallMapList* types) {
495   Handle<Object> object = GetInfo(ast_id);
496   if (object->IsUndefined() || object->IsSmi()) return;
497 
498   if (*object ==
499       isolate_->builtins()->builtin(Builtins::kStoreIC_GlobalProxy)) {
500     // TODO(fschneider): We could collect the maps and signal that
501     // we need a generic store (or load) here.
502     ASSERT(Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC);
503   } else if (object->IsMap()) {
504     types->Add(Handle<Map>::cast(object));
505   } else if (FLAG_collect_megamorphic_maps_from_stub_cache &&
506       Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) {
507     types->Reserve(4);
508     ASSERT(object->IsCode());
509     isolate_->stub_cache()->CollectMatchingMaps(types,
510                                                 *name,
511                                                 flags,
512                                                 global_context_);
513   }
514 }
515 
516 
517 // Check if a map originates from a given global context. We use this
518 // information to filter out maps from different context to avoid
519 // retaining objects from different tabs in Chrome via optimized code.
CanRetainOtherContext(Map * map,Context * global_context)520 bool TypeFeedbackOracle::CanRetainOtherContext(Map* map,
521                                                Context* global_context) {
522   Object* constructor = NULL;
523   while (!map->prototype()->IsNull()) {
524     constructor = map->constructor();
525     if (!constructor->IsNull()) {
526       // If the constructor is not null or a JSFunction, we have to
527       // conservatively assume that it may retain a global context.
528       if (!constructor->IsJSFunction()) return true;
529       // Check if the constructor directly references a foreign context.
530       if (CanRetainOtherContext(JSFunction::cast(constructor),
531                                 global_context)) {
532         return true;
533       }
534     }
535     map = HeapObject::cast(map->prototype())->map();
536   }
537   constructor = map->constructor();
538   if (constructor->IsNull()) return false;
539   JSFunction* function = JSFunction::cast(constructor);
540   return CanRetainOtherContext(function, global_context);
541 }
542 
543 
CanRetainOtherContext(JSFunction * function,Context * global_context)544 bool TypeFeedbackOracle::CanRetainOtherContext(JSFunction* function,
545                                                Context* global_context) {
546   return function->context()->global() != global_context->global()
547       && function->context()->global() != global_context->builtins();
548 }
549 
550 
AddMapIfMissing(Handle<Map> map,SmallMapList * list)551 static void AddMapIfMissing(Handle<Map> map, SmallMapList* list) {
552   for (int i = 0; i < list->length(); ++i) {
553     if (list->at(i).is_identical_to(map)) return;
554   }
555   list->Add(map);
556 }
557 
558 
CollectKeyedReceiverTypes(unsigned ast_id,SmallMapList * types)559 void TypeFeedbackOracle::CollectKeyedReceiverTypes(unsigned ast_id,
560                                                    SmallMapList* types) {
561   Handle<Object> object = GetInfo(ast_id);
562   if (!object->IsCode()) return;
563   Handle<Code> code = Handle<Code>::cast(object);
564   if (code->kind() == Code::KEYED_LOAD_IC ||
565       code->kind() == Code::KEYED_STORE_IC) {
566     AssertNoAllocation no_allocation;
567     int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
568     for (RelocIterator it(*code, mask); !it.done(); it.next()) {
569       RelocInfo* info = it.rinfo();
570       Object* object = info->target_object();
571       if (object->IsMap()) {
572         Map* map = Map::cast(object);
573         if (!CanRetainOtherContext(map, *global_context_)) {
574           AddMapIfMissing(Handle<Map>(map), types);
575         }
576       }
577     }
578   }
579 }
580 
581 
ToBooleanTypes(unsigned ast_id)582 byte TypeFeedbackOracle::ToBooleanTypes(unsigned ast_id) {
583   Handle<Object> object = GetInfo(ast_id);
584   return object->IsCode() ? Handle<Code>::cast(object)->to_boolean_state() : 0;
585 }
586 
587 
588 // Things are a bit tricky here: The iterator for the RelocInfos and the infos
589 // themselves are not GC-safe, so we first get all infos, then we create the
590 // dictionary (possibly triggering GC), and finally we relocate the collected
591 // infos before we process them.
BuildDictionary(Handle<Code> code)592 void TypeFeedbackOracle::BuildDictionary(Handle<Code> code) {
593   AssertNoAllocation no_allocation;
594   ZoneList<RelocInfo> infos(16);
595   HandleScope scope;
596   GetRelocInfos(code, &infos);
597   CreateDictionary(code, &infos);
598   ProcessRelocInfos(&infos);
599   ProcessTypeFeedbackCells(code);
600   // Allocate handle in the parent scope.
601   dictionary_ = scope.CloseAndEscape(dictionary_);
602 }
603 
604 
GetRelocInfos(Handle<Code> code,ZoneList<RelocInfo> * infos)605 void TypeFeedbackOracle::GetRelocInfos(Handle<Code> code,
606                                        ZoneList<RelocInfo>* infos) {
607   int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID);
608   for (RelocIterator it(*code, mask); !it.done(); it.next()) {
609     infos->Add(*it.rinfo());
610   }
611 }
612 
613 
CreateDictionary(Handle<Code> code,ZoneList<RelocInfo> * infos)614 void TypeFeedbackOracle::CreateDictionary(Handle<Code> code,
615                                           ZoneList<RelocInfo>* infos) {
616   DisableAssertNoAllocation allocation_allowed;
617   int cell_count = code->type_feedback_info()->IsTypeFeedbackInfo()
618       ? TypeFeedbackInfo::cast(code->type_feedback_info())->
619           type_feedback_cells()->CellCount()
620       : 0;
621   int length = infos->length() + cell_count;
622   byte* old_start = code->instruction_start();
623   dictionary_ = FACTORY->NewUnseededNumberDictionary(length);
624   byte* new_start = code->instruction_start();
625   RelocateRelocInfos(infos, old_start, new_start);
626 }
627 
628 
RelocateRelocInfos(ZoneList<RelocInfo> * infos,byte * old_start,byte * new_start)629 void TypeFeedbackOracle::RelocateRelocInfos(ZoneList<RelocInfo>* infos,
630                                             byte* old_start,
631                                             byte* new_start) {
632   for (int i = 0; i < infos->length(); i++) {
633     RelocInfo* info = &(*infos)[i];
634     info->set_pc(new_start + (info->pc() - old_start));
635   }
636 }
637 
638 
ProcessRelocInfos(ZoneList<RelocInfo> * infos)639 void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) {
640   for (int i = 0; i < infos->length(); i++) {
641     RelocInfo reloc_entry = (*infos)[i];
642     Address target_address = reloc_entry.target_address();
643     unsigned ast_id = static_cast<unsigned>((*infos)[i].data());
644     Code* target = Code::GetCodeFromTargetAddress(target_address);
645     switch (target->kind()) {
646       case Code::LOAD_IC:
647       case Code::STORE_IC:
648       case Code::CALL_IC:
649       case Code::KEYED_CALL_IC:
650         if (target->ic_state() == MONOMORPHIC) {
651           if (target->kind() == Code::CALL_IC &&
652               target->check_type() != RECEIVER_MAP_CHECK) {
653             SetInfo(ast_id, Smi::FromInt(target->check_type()));
654           } else {
655             Object* map = target->FindFirstMap();
656             if (map == NULL) {
657               SetInfo(ast_id, static_cast<Object*>(target));
658             } else if (!CanRetainOtherContext(Map::cast(map),
659                                               *global_context_)) {
660               SetInfo(ast_id, map);
661             }
662           }
663         } else {
664           SetInfo(ast_id, target);
665         }
666         break;
667 
668       case Code::KEYED_LOAD_IC:
669       case Code::KEYED_STORE_IC:
670         if (target->ic_state() == MONOMORPHIC ||
671             target->ic_state() == MEGAMORPHIC) {
672           SetInfo(ast_id, target);
673         }
674         break;
675 
676       case Code::UNARY_OP_IC:
677       case Code::BINARY_OP_IC:
678       case Code::COMPARE_IC:
679       case Code::TO_BOOLEAN_IC:
680         SetInfo(ast_id, target);
681         break;
682 
683       default:
684         break;
685     }
686   }
687 }
688 
689 
ProcessTypeFeedbackCells(Handle<Code> code)690 void TypeFeedbackOracle::ProcessTypeFeedbackCells(Handle<Code> code) {
691   Object* raw_info = code->type_feedback_info();
692   if (!raw_info->IsTypeFeedbackInfo()) return;
693   Handle<TypeFeedbackCells> cache(
694       TypeFeedbackInfo::cast(raw_info)->type_feedback_cells());
695   for (int i = 0; i < cache->CellCount(); i++) {
696     unsigned ast_id = cache->AstId(i)->value();
697     Object* value = cache->Cell(i)->value();
698     if (value->IsSmi() ||
699         (value->IsJSFunction() &&
700          !CanRetainOtherContext(JSFunction::cast(value),
701                                 *global_context_))) {
702       SetInfo(ast_id, value);
703     }
704   }
705 }
706 
707 
SetInfo(unsigned ast_id,Object * target)708 void TypeFeedbackOracle::SetInfo(unsigned ast_id, Object* target) {
709   ASSERT(dictionary_->FindEntry(ast_id) == UnseededNumberDictionary::kNotFound);
710   MaybeObject* maybe_result = dictionary_->AtNumberPut(ast_id, target);
711   USE(maybe_result);
712 #ifdef DEBUG
713   Object* result = NULL;
714   // Dictionary has been allocated with sufficient size for all elements.
715   ASSERT(maybe_result->ToObject(&result));
716   ASSERT(*dictionary_ == result);
717 #endif
718 }
719 
720 } }  // namespace v8::internal
721