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