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