1 // Copyright 2014 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/objects/feedback-vector.h"
6
7 #include "src/common/globals.h"
8 #include "src/deoptimizer/deoptimizer.h"
9 #include "src/diagnostics/code-tracer.h"
10 #include "src/heap/heap-inl.h"
11 #include "src/heap/local-factory-inl.h"
12 #include "src/ic/handler-configuration-inl.h"
13 #include "src/ic/ic-inl.h"
14 #include "src/objects/data-handler-inl.h"
15 #include "src/objects/feedback-vector-inl.h"
16 #include "src/objects/hash-table-inl.h"
17 #include "src/objects/map-inl.h"
18 #include "src/objects/object-macros.h"
19 #include "src/objects/objects.h"
20
21 namespace v8 {
22 namespace internal {
23
AddSlot(FeedbackSlotKind kind)24 FeedbackSlot FeedbackVectorSpec::AddSlot(FeedbackSlotKind kind) {
25 int slot = slot_count();
26 int entries_per_slot = FeedbackMetadata::GetSlotSize(kind);
27 append(kind);
28 for (int i = 1; i < entries_per_slot; i++) {
29 append(FeedbackSlotKind::kInvalid);
30 }
31 return FeedbackSlot(slot);
32 }
33
AddTypeProfileSlot()34 FeedbackSlot FeedbackVectorSpec::AddTypeProfileSlot() {
35 FeedbackSlot slot = AddSlot(FeedbackSlotKind::kTypeProfile);
36 CHECK_EQ(FeedbackVectorSpec::kTypeProfileSlotIndex,
37 FeedbackVector::GetIndex(slot));
38 return slot;
39 }
40
HasTypeProfileSlot() const41 bool FeedbackVectorSpec::HasTypeProfileSlot() const {
42 FeedbackSlot slot =
43 FeedbackVector::ToSlot(FeedbackVectorSpec::kTypeProfileSlotIndex);
44 if (slot_count() <= slot.ToInt()) return false;
45 return GetKind(slot) == FeedbackSlotKind::kTypeProfile;
46 }
47
IsPropertyNameFeedback(MaybeObject feedback)48 static bool IsPropertyNameFeedback(MaybeObject feedback) {
49 HeapObject heap_object;
50 if (!feedback->GetHeapObjectIfStrong(&heap_object)) return false;
51 if (heap_object.IsString()) {
52 DCHECK(heap_object.IsInternalizedString());
53 return true;
54 }
55 if (!heap_object.IsSymbol()) return false;
56 Symbol symbol = Symbol::cast(heap_object);
57 ReadOnlyRoots roots = symbol.GetReadOnlyRoots();
58 return symbol != roots.uninitialized_symbol() &&
59 symbol != roots.mega_dom_symbol() &&
60 symbol != roots.megamorphic_symbol();
61 }
62
operator <<(std::ostream & os,FeedbackSlotKind kind)63 std::ostream& operator<<(std::ostream& os, FeedbackSlotKind kind) {
64 return os << FeedbackMetadata::Kind2String(kind);
65 }
66
GetKind(FeedbackSlot slot) const67 FeedbackSlotKind FeedbackMetadata::GetKind(FeedbackSlot slot) const {
68 int index = VectorICComputer::index(0, slot.ToInt());
69 int data = get(index);
70 return VectorICComputer::decode(data, slot.ToInt());
71 }
72
SetKind(FeedbackSlot slot,FeedbackSlotKind kind)73 void FeedbackMetadata::SetKind(FeedbackSlot slot, FeedbackSlotKind kind) {
74 int index = VectorICComputer::index(0, slot.ToInt());
75 int data = get(index);
76 int new_data = VectorICComputer::encode(data, slot.ToInt(), kind);
77 set(index, new_data);
78 }
79
80 // static
81 template <typename IsolateT>
New(IsolateT * isolate,const FeedbackVectorSpec * spec)82 Handle<FeedbackMetadata> FeedbackMetadata::New(IsolateT* isolate,
83 const FeedbackVectorSpec* spec) {
84 auto* factory = isolate->factory();
85
86 const int slot_count = spec == nullptr ? 0 : spec->slot_count();
87 const int create_closure_slot_count =
88 spec == nullptr ? 0 : spec->create_closure_slot_count();
89 if (slot_count == 0 && create_closure_slot_count == 0) {
90 return factory->empty_feedback_metadata();
91 }
92 #ifdef DEBUG
93 for (int i = 0; i < slot_count;) {
94 DCHECK(spec);
95 FeedbackSlotKind kind = spec->GetKind(FeedbackSlot(i));
96 int entry_size = FeedbackMetadata::GetSlotSize(kind);
97 for (int j = 1; j < entry_size; j++) {
98 kind = spec->GetKind(FeedbackSlot(i + j));
99 DCHECK_EQ(FeedbackSlotKind::kInvalid, kind);
100 }
101 i += entry_size;
102 }
103 #endif
104
105 Handle<FeedbackMetadata> metadata =
106 factory->NewFeedbackMetadata(slot_count, create_closure_slot_count);
107
108 // Initialize the slots. The raw data section has already been pre-zeroed in
109 // NewFeedbackMetadata.
110 for (int i = 0; i < slot_count; i++) {
111 DCHECK(spec);
112 FeedbackSlot slot(i);
113 FeedbackSlotKind kind = spec->GetKind(slot);
114 metadata->SetKind(slot, kind);
115 }
116
117 return metadata;
118 }
119
120 template Handle<FeedbackMetadata> FeedbackMetadata::New(
121 Isolate* isolate, const FeedbackVectorSpec* spec);
122 template Handle<FeedbackMetadata> FeedbackMetadata::New(
123 LocalIsolate* isolate, const FeedbackVectorSpec* spec);
124
SpecDiffersFrom(const FeedbackVectorSpec * other_spec) const125 bool FeedbackMetadata::SpecDiffersFrom(
126 const FeedbackVectorSpec* other_spec) const {
127 if (other_spec->slot_count() != slot_count()) {
128 return true;
129 }
130
131 int slots = slot_count();
132 for (int i = 0; i < slots;) {
133 FeedbackSlot slot(i);
134 FeedbackSlotKind kind = GetKind(slot);
135 int entry_size = FeedbackMetadata::GetSlotSize(kind);
136
137 if (kind != other_spec->GetKind(slot)) {
138 return true;
139 }
140 i += entry_size;
141 }
142 return false;
143 }
144
Kind2String(FeedbackSlotKind kind)145 const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) {
146 switch (kind) {
147 case FeedbackSlotKind::kInvalid:
148 return "Invalid";
149 case FeedbackSlotKind::kCall:
150 return "Call";
151 case FeedbackSlotKind::kLoadProperty:
152 return "LoadProperty";
153 case FeedbackSlotKind::kLoadGlobalInsideTypeof:
154 return "LoadGlobalInsideTypeof";
155 case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
156 return "LoadGlobalNotInsideTypeof";
157 case FeedbackSlotKind::kLoadKeyed:
158 return "LoadKeyed";
159 case FeedbackSlotKind::kHasKeyed:
160 return "HasKeyed";
161 case FeedbackSlotKind::kSetNamedSloppy:
162 return "SetNamedSloppy";
163 case FeedbackSlotKind::kSetNamedStrict:
164 return "SetNamedStrict";
165 case FeedbackSlotKind::kDefineNamedOwn:
166 return "DefineNamedOwn";
167 case FeedbackSlotKind::kDefineKeyedOwn:
168 return "DefineKeyedOwn";
169 case FeedbackSlotKind::kStoreGlobalSloppy:
170 return "StoreGlobalSloppy";
171 case FeedbackSlotKind::kStoreGlobalStrict:
172 return "StoreGlobalStrict";
173 case FeedbackSlotKind::kSetKeyedSloppy:
174 return "StoreKeyedSloppy";
175 case FeedbackSlotKind::kSetKeyedStrict:
176 return "StoreKeyedStrict";
177 case FeedbackSlotKind::kStoreInArrayLiteral:
178 return "StoreInArrayLiteral";
179 case FeedbackSlotKind::kBinaryOp:
180 return "BinaryOp";
181 case FeedbackSlotKind::kCompareOp:
182 return "CompareOp";
183 case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral:
184 return "DefineKeyedOwnPropertyInLiteral";
185 case FeedbackSlotKind::kLiteral:
186 return "Literal";
187 case FeedbackSlotKind::kTypeProfile:
188 return "TypeProfile";
189 case FeedbackSlotKind::kForIn:
190 return "ForIn";
191 case FeedbackSlotKind::kInstanceOf:
192 return "InstanceOf";
193 case FeedbackSlotKind::kCloneObject:
194 return "CloneObject";
195 case FeedbackSlotKind::kKindsNumber:
196 break;
197 }
198 UNREACHABLE();
199 }
200
HasTypeProfileSlot() const201 bool FeedbackMetadata::HasTypeProfileSlot() const {
202 FeedbackSlot slot =
203 FeedbackVector::ToSlot(FeedbackVectorSpec::kTypeProfileSlotIndex);
204 return slot.ToInt() < slot_count() &&
205 GetKind(slot) == FeedbackSlotKind::kTypeProfile;
206 }
207
GetKind(FeedbackSlot slot) const208 FeedbackSlotKind FeedbackVector::GetKind(FeedbackSlot slot) const {
209 DCHECK(!is_empty());
210 return metadata().GetKind(slot);
211 }
212
GetKind(FeedbackSlot slot,AcquireLoadTag tag) const213 FeedbackSlotKind FeedbackVector::GetKind(FeedbackSlot slot,
214 AcquireLoadTag tag) const {
215 DCHECK(!is_empty());
216 return metadata(tag).GetKind(slot);
217 }
218
GetTypeProfileSlot() const219 FeedbackSlot FeedbackVector::GetTypeProfileSlot() const {
220 DCHECK(metadata().HasTypeProfileSlot());
221 FeedbackSlot slot =
222 FeedbackVector::ToSlot(FeedbackVectorSpec::kTypeProfileSlotIndex);
223 DCHECK_EQ(FeedbackSlotKind::kTypeProfile, GetKind(slot));
224 return slot;
225 }
226
227 // static
New(Isolate * isolate,Handle<SharedFunctionInfo> shared)228 Handle<ClosureFeedbackCellArray> ClosureFeedbackCellArray::New(
229 Isolate* isolate, Handle<SharedFunctionInfo> shared) {
230 Factory* factory = isolate->factory();
231
232 int num_feedback_cells =
233 shared->feedback_metadata().create_closure_slot_count();
234
235 Handle<ClosureFeedbackCellArray> feedback_cell_array =
236 factory->NewClosureFeedbackCellArray(num_feedback_cells);
237
238 for (int i = 0; i < num_feedback_cells; i++) {
239 Handle<FeedbackCell> cell =
240 factory->NewNoClosuresCell(factory->undefined_value());
241 feedback_cell_array->set(i, *cell);
242 }
243 return feedback_cell_array;
244 }
245
246 // static
New(Isolate * isolate,Handle<SharedFunctionInfo> shared,Handle<ClosureFeedbackCellArray> closure_feedback_cell_array,IsCompiledScope * is_compiled_scope)247 Handle<FeedbackVector> FeedbackVector::New(
248 Isolate* isolate, Handle<SharedFunctionInfo> shared,
249 Handle<ClosureFeedbackCellArray> closure_feedback_cell_array,
250 IsCompiledScope* is_compiled_scope) {
251 DCHECK(is_compiled_scope->is_compiled());
252 Factory* factory = isolate->factory();
253
254 Handle<FeedbackMetadata> feedback_metadata(shared->feedback_metadata(),
255 isolate);
256 const int slot_count = feedback_metadata->slot_count();
257
258 Handle<FeedbackVector> vector =
259 factory->NewFeedbackVector(shared, closure_feedback_cell_array);
260
261 DCHECK_EQ(vector->length(), slot_count);
262
263 DCHECK_EQ(vector->shared_function_info(), *shared);
264 DCHECK_EQ(vector->tiering_state(), TieringState::kNone);
265 DCHECK(!vector->maybe_has_optimized_code());
266 DCHECK_EQ(vector->invocation_count(), 0);
267 DCHECK_EQ(vector->profiler_ticks(), 0);
268 DCHECK(vector->maybe_optimized_code()->IsCleared());
269
270 // Ensure we can skip the write barrier
271 Handle<Symbol> uninitialized_sentinel = UninitializedSentinel(isolate);
272 DCHECK_EQ(ReadOnlyRoots(isolate).uninitialized_symbol(),
273 *uninitialized_sentinel);
274 for (int i = 0; i < slot_count;) {
275 FeedbackSlot slot(i);
276 FeedbackSlotKind kind = feedback_metadata->GetKind(slot);
277 int entry_size = FeedbackMetadata::GetSlotSize(kind);
278
279 MaybeObject extra_value = MaybeObject::FromObject(*uninitialized_sentinel);
280 switch (kind) {
281 case FeedbackSlotKind::kLoadGlobalInsideTypeof:
282 case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
283 case FeedbackSlotKind::kStoreGlobalSloppy:
284 case FeedbackSlotKind::kStoreGlobalStrict:
285 vector->Set(slot, HeapObjectReference::ClearedValue(isolate),
286 SKIP_WRITE_BARRIER);
287 break;
288 case FeedbackSlotKind::kForIn:
289 case FeedbackSlotKind::kCompareOp:
290 case FeedbackSlotKind::kBinaryOp:
291 vector->Set(slot, Smi::zero(), SKIP_WRITE_BARRIER);
292 break;
293 case FeedbackSlotKind::kLiteral:
294 vector->Set(slot, Smi::zero(), SKIP_WRITE_BARRIER);
295 break;
296 case FeedbackSlotKind::kCall:
297 vector->Set(slot, *uninitialized_sentinel, SKIP_WRITE_BARRIER);
298 extra_value = MaybeObject::FromObject(Smi::zero());
299 break;
300 case FeedbackSlotKind::kCloneObject:
301 case FeedbackSlotKind::kLoadProperty:
302 case FeedbackSlotKind::kLoadKeyed:
303 case FeedbackSlotKind::kHasKeyed:
304 case FeedbackSlotKind::kSetNamedSloppy:
305 case FeedbackSlotKind::kSetNamedStrict:
306 case FeedbackSlotKind::kDefineNamedOwn:
307 case FeedbackSlotKind::kDefineKeyedOwn:
308 case FeedbackSlotKind::kSetKeyedSloppy:
309 case FeedbackSlotKind::kSetKeyedStrict:
310 case FeedbackSlotKind::kStoreInArrayLiteral:
311 case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral:
312 case FeedbackSlotKind::kTypeProfile:
313 case FeedbackSlotKind::kInstanceOf:
314 vector->Set(slot, *uninitialized_sentinel, SKIP_WRITE_BARRIER);
315 break;
316
317 case FeedbackSlotKind::kInvalid:
318 case FeedbackSlotKind::kKindsNumber:
319 UNREACHABLE();
320 }
321 for (int j = 1; j < entry_size; j++) {
322 vector->Set(slot.WithOffset(j), extra_value, SKIP_WRITE_BARRIER);
323 }
324 i += entry_size;
325 }
326
327 Handle<FeedbackVector> result = Handle<FeedbackVector>::cast(vector);
328 if (!isolate->is_best_effort_code_coverage() ||
329 isolate->is_collecting_type_profile()) {
330 AddToVectorsForProfilingTools(isolate, result);
331 }
332 return result;
333 }
334
335 namespace {
336
NewFeedbackVectorForTesting(Isolate * isolate,const FeedbackVectorSpec * spec)337 Handle<FeedbackVector> NewFeedbackVectorForTesting(
338 Isolate* isolate, const FeedbackVectorSpec* spec) {
339 Handle<FeedbackMetadata> metadata = FeedbackMetadata::New(isolate, spec);
340 Handle<SharedFunctionInfo> shared =
341 isolate->factory()->NewSharedFunctionInfoForBuiltin(
342 isolate->factory()->empty_string(), Builtin::kIllegal);
343 // Set the raw feedback metadata to circumvent checks that we are not
344 // overwriting existing metadata.
345 shared->set_raw_outer_scope_info_or_feedback_metadata(*metadata);
346 Handle<ClosureFeedbackCellArray> closure_feedback_cell_array =
347 ClosureFeedbackCellArray::New(isolate, shared);
348
349 IsCompiledScope is_compiled_scope(shared->is_compiled_scope(isolate));
350 return FeedbackVector::New(isolate, shared, closure_feedback_cell_array,
351 &is_compiled_scope);
352 }
353
354 } // namespace
355
356 // static
NewWithOneBinarySlotForTesting(Zone * zone,Isolate * isolate)357 Handle<FeedbackVector> FeedbackVector::NewWithOneBinarySlotForTesting(
358 Zone* zone, Isolate* isolate) {
359 FeedbackVectorSpec one_slot(zone);
360 one_slot.AddBinaryOpICSlot();
361 return NewFeedbackVectorForTesting(isolate, &one_slot);
362 }
363
364 // static
NewWithOneCompareSlotForTesting(Zone * zone,Isolate * isolate)365 Handle<FeedbackVector> FeedbackVector::NewWithOneCompareSlotForTesting(
366 Zone* zone, Isolate* isolate) {
367 FeedbackVectorSpec one_slot(zone);
368 one_slot.AddCompareICSlot();
369 return NewFeedbackVectorForTesting(isolate, &one_slot);
370 }
371
372 // static
AddToVectorsForProfilingTools(Isolate * isolate,Handle<FeedbackVector> vector)373 void FeedbackVector::AddToVectorsForProfilingTools(
374 Isolate* isolate, Handle<FeedbackVector> vector) {
375 DCHECK(!isolate->is_best_effort_code_coverage() ||
376 isolate->is_collecting_type_profile());
377 if (!vector->shared_function_info().IsSubjectToDebugging()) return;
378 Handle<ArrayList> list = Handle<ArrayList>::cast(
379 isolate->factory()->feedback_vectors_for_profiling_tools());
380 list = ArrayList::Add(isolate, list, vector);
381 isolate->SetFeedbackVectorsForProfilingTools(*list);
382 }
383
SaturatingIncrementProfilerTicks()384 void FeedbackVector::SaturatingIncrementProfilerTicks() {
385 int ticks = profiler_ticks();
386 if (ticks < Smi::kMaxValue) set_profiler_ticks(ticks + 1);
387 }
388
SetOptimizedCode(Handle<CodeT> code)389 void FeedbackVector::SetOptimizedCode(Handle<CodeT> code) {
390 DCHECK(CodeKindIsOptimizedJSFunction(code->kind()));
391 // We should set optimized code only when there is no valid optimized code.
392 DCHECK(!has_optimized_code() ||
393 optimized_code().marked_for_deoptimization() ||
394 FLAG_stress_concurrent_inlining_attach_code);
395 // TODO(mythria): We could see a CompileOptimized state here either from
396 // tests that use %OptimizeFunctionOnNextCall, --always-opt or because we
397 // re-mark the function for non-concurrent optimization after an OSR. We
398 // should avoid these cases and also check that marker isn't
399 // TieringState::kRequestTurbofan*.
400 set_maybe_optimized_code(HeapObjectReference::Weak(*code), kReleaseStore);
401 int32_t state = flags();
402 state = TieringStateBits::update(state, TieringState::kNone);
403 state = MaybeHasOptimizedCodeBit::update(state, true);
404 set_flags(state);
405 }
406
ClearOptimizedCode()407 void FeedbackVector::ClearOptimizedCode() {
408 DCHECK(has_optimized_code());
409 DCHECK(maybe_has_optimized_code());
410 set_maybe_optimized_code(HeapObjectReference::ClearedValue(GetIsolate()),
411 kReleaseStore);
412 set_maybe_has_optimized_code(false);
413 }
414
reset_tiering_state()415 void FeedbackVector::reset_tiering_state() {
416 set_tiering_state(TieringState::kNone);
417 }
418
set_tiering_state(TieringState state)419 void FeedbackVector::set_tiering_state(TieringState state) {
420 int32_t new_flags = flags();
421 new_flags = TieringStateBits::update(new_flags, state);
422 set_flags(new_flags);
423 }
424
reset_flags()425 void FeedbackVector::reset_flags() {
426 set_flags(TieringStateBits::encode(TieringState::kNone) |
427 OsrTieringStateBit::encode(TieringState::kNone) |
428 MaybeHasOptimizedCodeBit::encode(false));
429 }
430
osr_tiering_state()431 TieringState FeedbackVector::osr_tiering_state() {
432 return OsrTieringStateBit::decode(flags());
433 }
434
set_osr_tiering_state(TieringState marker)435 void FeedbackVector::set_osr_tiering_state(TieringState marker) {
436 DCHECK(marker == TieringState::kNone || marker == TieringState::kInProgress);
437 STATIC_ASSERT(TieringState::kNone <= OsrTieringStateBit::kMax);
438 STATIC_ASSERT(TieringState::kInProgress <= OsrTieringStateBit::kMax);
439 int32_t state = flags();
440 state = OsrTieringStateBit::update(state, marker);
441 set_flags(state);
442 }
443
EvictOptimizedCodeMarkedForDeoptimization(SharedFunctionInfo shared,const char * reason)444 void FeedbackVector::EvictOptimizedCodeMarkedForDeoptimization(
445 SharedFunctionInfo shared, const char* reason) {
446 MaybeObject slot = maybe_optimized_code(kAcquireLoad);
447 if (slot->IsCleared()) {
448 set_maybe_has_optimized_code(false);
449 return;
450 }
451
452 Code code = FromCodeT(CodeT::cast(slot->GetHeapObject()));
453 if (code.marked_for_deoptimization()) {
454 Deoptimizer::TraceEvictFromOptimizedCodeCache(shared, reason);
455 ClearOptimizedCode();
456 }
457 }
458
ClearSlots(Isolate * isolate)459 bool FeedbackVector::ClearSlots(Isolate* isolate) {
460 if (!shared_function_info().HasFeedbackMetadata()) return false;
461 MaybeObject uninitialized_sentinel = MaybeObject::FromObject(
462 FeedbackVector::RawUninitializedSentinel(isolate));
463
464 bool feedback_updated = false;
465 FeedbackMetadataIterator iter(metadata());
466 while (iter.HasNext()) {
467 FeedbackSlot slot = iter.Next();
468
469 MaybeObject obj = Get(slot);
470 if (obj != uninitialized_sentinel) {
471 FeedbackNexus nexus(*this, slot);
472 feedback_updated |= nexus.Clear();
473 }
474 }
475 return feedback_updated;
476 }
477
NewHandle(MaybeObject object) const478 MaybeObjectHandle NexusConfig::NewHandle(MaybeObject object) const {
479 if (mode() == Mode::MainThread) {
480 return handle(object, isolate_);
481 }
482 DCHECK_EQ(mode(), Mode::BackgroundThread);
483 return handle(object, local_heap_);
484 }
485
486 template <typename T>
NewHandle(T object) const487 Handle<T> NexusConfig::NewHandle(T object) const {
488 if (mode() == Mode::MainThread) {
489 return handle(object, isolate_);
490 }
491 DCHECK_EQ(mode(), Mode::BackgroundThread);
492 return handle(object, local_heap_);
493 }
494
SetFeedbackPair(FeedbackVector vector,FeedbackSlot start_slot,MaybeObject feedback,WriteBarrierMode mode,MaybeObject feedback_extra,WriteBarrierMode mode_extra) const495 void NexusConfig::SetFeedbackPair(FeedbackVector vector,
496 FeedbackSlot start_slot, MaybeObject feedback,
497 WriteBarrierMode mode,
498 MaybeObject feedback_extra,
499 WriteBarrierMode mode_extra) const {
500 CHECK(can_write());
501 CHECK_GT(vector.length(), start_slot.WithOffset(1).ToInt());
502 base::SharedMutexGuard<base::kExclusive> shared_mutex_guard(
503 isolate()->feedback_vector_access());
504 vector.Set(start_slot, feedback, mode);
505 vector.Set(start_slot.WithOffset(1), feedback_extra, mode_extra);
506 }
507
GetFeedbackPair(FeedbackVector vector,FeedbackSlot slot) const508 std::pair<MaybeObject, MaybeObject> NexusConfig::GetFeedbackPair(
509 FeedbackVector vector, FeedbackSlot slot) const {
510 base::SharedMutexGuardIf<base::kShared> scope(
511 isolate()->feedback_vector_access(), mode() == BackgroundThread);
512 MaybeObject feedback = vector.Get(slot);
513 MaybeObject feedback_extra = vector.Get(slot.WithOffset(1));
514 return std::make_pair(feedback, feedback_extra);
515 }
516
FeedbackNexus(Handle<FeedbackVector> vector,FeedbackSlot slot)517 FeedbackNexus::FeedbackNexus(Handle<FeedbackVector> vector, FeedbackSlot slot)
518 : vector_handle_(vector),
519 slot_(slot),
520 config_(NexusConfig::FromMainThread(
521 vector.is_null() ? nullptr : vector->GetIsolate())) {
522 kind_ = vector.is_null() ? FeedbackSlotKind::kInvalid : vector->GetKind(slot);
523 }
524
FeedbackNexus(FeedbackVector vector,FeedbackSlot slot)525 FeedbackNexus::FeedbackNexus(FeedbackVector vector, FeedbackSlot slot)
526 : vector_(vector),
527 slot_(slot),
528 config_(NexusConfig::FromMainThread(
529 vector.is_null() ? nullptr : vector.GetIsolate())) {
530 kind_ = vector.is_null() ? FeedbackSlotKind::kInvalid : vector.GetKind(slot);
531 }
532
FeedbackNexus(Handle<FeedbackVector> vector,FeedbackSlot slot,const NexusConfig & config)533 FeedbackNexus::FeedbackNexus(Handle<FeedbackVector> vector, FeedbackSlot slot,
534 const NexusConfig& config)
535 : vector_handle_(vector),
536 slot_(slot),
537 kind_(vector->GetKind(slot, kAcquireLoad)),
538 config_(config) {}
539
CreateArrayOfSize(int length)540 Handle<WeakFixedArray> FeedbackNexus::CreateArrayOfSize(int length) {
541 DCHECK(config()->can_write());
542 Handle<WeakFixedArray> array =
543 GetIsolate()->factory()->NewWeakFixedArray(length);
544 return array;
545 }
546
ConfigureUninitialized()547 void FeedbackNexus::ConfigureUninitialized() {
548 Isolate* isolate = GetIsolate();
549 switch (kind()) {
550 case FeedbackSlotKind::kStoreGlobalSloppy:
551 case FeedbackSlotKind::kStoreGlobalStrict:
552 case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
553 case FeedbackSlotKind::kLoadGlobalInsideTypeof: {
554 SetFeedback(HeapObjectReference::ClearedValue(isolate),
555 SKIP_WRITE_BARRIER, UninitializedSentinel(),
556 SKIP_WRITE_BARRIER);
557 break;
558 }
559 case FeedbackSlotKind::kCloneObject:
560 case FeedbackSlotKind::kCall: {
561 SetFeedback(UninitializedSentinel(), SKIP_WRITE_BARRIER, Smi::zero(),
562 SKIP_WRITE_BARRIER);
563 break;
564 }
565 case FeedbackSlotKind::kInstanceOf: {
566 SetFeedback(UninitializedSentinel(), SKIP_WRITE_BARRIER);
567 break;
568 }
569 case FeedbackSlotKind::kSetNamedSloppy:
570 case FeedbackSlotKind::kSetNamedStrict:
571 case FeedbackSlotKind::kSetKeyedSloppy:
572 case FeedbackSlotKind::kSetKeyedStrict:
573 case FeedbackSlotKind::kStoreInArrayLiteral:
574 case FeedbackSlotKind::kDefineNamedOwn:
575 case FeedbackSlotKind::kDefineKeyedOwn:
576 case FeedbackSlotKind::kLoadProperty:
577 case FeedbackSlotKind::kLoadKeyed:
578 case FeedbackSlotKind::kHasKeyed:
579 case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral: {
580 SetFeedback(UninitializedSentinel(), SKIP_WRITE_BARRIER,
581 UninitializedSentinel(), SKIP_WRITE_BARRIER);
582 break;
583 }
584 default:
585 UNREACHABLE();
586 }
587 }
588
Clear()589 bool FeedbackNexus::Clear() {
590 bool feedback_updated = false;
591
592 switch (kind()) {
593 case FeedbackSlotKind::kTypeProfile:
594 // We don't clear these kinds ever.
595 break;
596
597 case FeedbackSlotKind::kCompareOp:
598 case FeedbackSlotKind::kForIn:
599 case FeedbackSlotKind::kBinaryOp:
600 // We don't clear these, either.
601 break;
602
603 case FeedbackSlotKind::kLiteral:
604 SetFeedback(Smi::zero(), SKIP_WRITE_BARRIER);
605 feedback_updated = true;
606 break;
607
608 case FeedbackSlotKind::kSetNamedSloppy:
609 case FeedbackSlotKind::kSetNamedStrict:
610 case FeedbackSlotKind::kSetKeyedSloppy:
611 case FeedbackSlotKind::kSetKeyedStrict:
612 case FeedbackSlotKind::kStoreInArrayLiteral:
613 case FeedbackSlotKind::kDefineNamedOwn:
614 case FeedbackSlotKind::kDefineKeyedOwn:
615 case FeedbackSlotKind::kLoadProperty:
616 case FeedbackSlotKind::kLoadKeyed:
617 case FeedbackSlotKind::kHasKeyed:
618 case FeedbackSlotKind::kStoreGlobalSloppy:
619 case FeedbackSlotKind::kStoreGlobalStrict:
620 case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
621 case FeedbackSlotKind::kLoadGlobalInsideTypeof:
622 case FeedbackSlotKind::kCall:
623 case FeedbackSlotKind::kInstanceOf:
624 case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral:
625 case FeedbackSlotKind::kCloneObject:
626 if (!IsCleared()) {
627 ConfigureUninitialized();
628 feedback_updated = true;
629 }
630 break;
631
632 case FeedbackSlotKind::kInvalid:
633 case FeedbackSlotKind::kKindsNumber:
634 UNREACHABLE();
635 }
636 return feedback_updated;
637 }
638
ConfigureMegamorphic()639 bool FeedbackNexus::ConfigureMegamorphic() {
640 DisallowGarbageCollection no_gc;
641 Isolate* isolate = GetIsolate();
642 MaybeObject sentinel = MegamorphicSentinel();
643 if (GetFeedback() != sentinel) {
644 SetFeedback(sentinel, SKIP_WRITE_BARRIER,
645 HeapObjectReference::ClearedValue(isolate));
646 return true;
647 }
648
649 return false;
650 }
651
ConfigureMegaDOM(const MaybeObjectHandle & handler)652 void FeedbackNexus::ConfigureMegaDOM(const MaybeObjectHandle& handler) {
653 DisallowGarbageCollection no_gc;
654 MaybeObject sentinel = MegaDOMSentinel();
655
656 SetFeedback(sentinel, SKIP_WRITE_BARRIER, *handler, UPDATE_WRITE_BARRIER);
657 }
658
ConfigureMegamorphic(IcCheckType property_type)659 bool FeedbackNexus::ConfigureMegamorphic(IcCheckType property_type) {
660 DisallowGarbageCollection no_gc;
661 MaybeObject sentinel = MegamorphicSentinel();
662 MaybeObject maybe_extra =
663 MaybeObject::FromSmi(Smi::FromInt(static_cast<int>(property_type)));
664
665 auto feedback = GetFeedbackPair();
666 bool update_required =
667 feedback.first != sentinel || feedback.second != maybe_extra;
668 if (update_required) {
669 SetFeedback(sentinel, SKIP_WRITE_BARRIER, maybe_extra, SKIP_WRITE_BARRIER);
670 }
671 return update_required;
672 }
673
GetFirstMap() const674 Map FeedbackNexus::GetFirstMap() const {
675 FeedbackIterator it(this);
676 if (!it.done()) {
677 return it.map();
678 }
679
680 return Map();
681 }
682
ic_state() const683 InlineCacheState FeedbackNexus::ic_state() const {
684 MaybeObject feedback, extra;
685 std::tie(feedback, extra) = GetFeedbackPair();
686
687 switch (kind()) {
688 case FeedbackSlotKind::kLiteral:
689 if (feedback->IsSmi()) return InlineCacheState::UNINITIALIZED;
690 return InlineCacheState::MONOMORPHIC;
691
692 case FeedbackSlotKind::kStoreGlobalSloppy:
693 case FeedbackSlotKind::kStoreGlobalStrict:
694 case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
695 case FeedbackSlotKind::kLoadGlobalInsideTypeof: {
696 if (feedback->IsSmi()) return InlineCacheState::MONOMORPHIC;
697
698 DCHECK(feedback->IsWeakOrCleared());
699 if (!feedback->IsCleared() || extra != UninitializedSentinel()) {
700 return InlineCacheState::MONOMORPHIC;
701 }
702 return InlineCacheState::UNINITIALIZED;
703 }
704
705 case FeedbackSlotKind::kSetNamedSloppy:
706 case FeedbackSlotKind::kSetNamedStrict:
707 case FeedbackSlotKind::kSetKeyedSloppy:
708 case FeedbackSlotKind::kSetKeyedStrict:
709 case FeedbackSlotKind::kStoreInArrayLiteral:
710 case FeedbackSlotKind::kDefineNamedOwn:
711 case FeedbackSlotKind::kDefineKeyedOwn:
712 case FeedbackSlotKind::kLoadProperty:
713 case FeedbackSlotKind::kLoadKeyed:
714 case FeedbackSlotKind::kHasKeyed: {
715 if (feedback == UninitializedSentinel()) {
716 return InlineCacheState::UNINITIALIZED;
717 }
718 if (feedback == MegamorphicSentinel()) {
719 return InlineCacheState::MEGAMORPHIC;
720 }
721 if (feedback == MegaDOMSentinel()) {
722 DCHECK(IsLoadICKind(kind()));
723 return InlineCacheState::MEGADOM;
724 }
725 if (feedback->IsWeakOrCleared()) {
726 // Don't check if the map is cleared.
727 return InlineCacheState::MONOMORPHIC;
728 }
729 HeapObject heap_object;
730 if (feedback->GetHeapObjectIfStrong(&heap_object)) {
731 if (heap_object.IsWeakFixedArray()) {
732 // Determine state purely by our structure, don't check if the maps
733 // are cleared.
734 return InlineCacheState::POLYMORPHIC;
735 }
736 if (heap_object.IsName()) {
737 DCHECK(IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) ||
738 IsKeyedHasICKind(kind()) || IsDefineKeyedOwnICKind(kind()));
739 Object extra_object = extra->GetHeapObjectAssumeStrong();
740 WeakFixedArray extra_array = WeakFixedArray::cast(extra_object);
741 return extra_array.length() > 2 ? InlineCacheState::POLYMORPHIC
742 : InlineCacheState::MONOMORPHIC;
743 }
744 }
745 UNREACHABLE();
746 }
747 case FeedbackSlotKind::kCall: {
748 HeapObject heap_object;
749 if (feedback == MegamorphicSentinel()) {
750 return InlineCacheState::GENERIC;
751 } else if (feedback->IsWeakOrCleared()) {
752 if (feedback->GetHeapObjectIfWeak(&heap_object)) {
753 if (heap_object.IsFeedbackCell()) {
754 return InlineCacheState::POLYMORPHIC;
755 }
756 CHECK(heap_object.IsJSFunction() || heap_object.IsJSBoundFunction());
757 }
758 return InlineCacheState::MONOMORPHIC;
759 } else if (feedback->GetHeapObjectIfStrong(&heap_object) &&
760 heap_object.IsAllocationSite()) {
761 return InlineCacheState::MONOMORPHIC;
762 }
763
764 CHECK_EQ(feedback, UninitializedSentinel());
765 return InlineCacheState::UNINITIALIZED;
766 }
767 case FeedbackSlotKind::kBinaryOp: {
768 BinaryOperationHint hint = GetBinaryOperationFeedback();
769 if (hint == BinaryOperationHint::kNone) {
770 return InlineCacheState::UNINITIALIZED;
771 } else if (hint == BinaryOperationHint::kAny) {
772 return InlineCacheState::GENERIC;
773 }
774
775 return InlineCacheState::MONOMORPHIC;
776 }
777 case FeedbackSlotKind::kCompareOp: {
778 CompareOperationHint hint = GetCompareOperationFeedback();
779 if (hint == CompareOperationHint::kNone) {
780 return InlineCacheState::UNINITIALIZED;
781 } else if (hint == CompareOperationHint::kAny) {
782 return InlineCacheState::GENERIC;
783 }
784
785 return InlineCacheState::MONOMORPHIC;
786 }
787 case FeedbackSlotKind::kForIn: {
788 ForInHint hint = GetForInFeedback();
789 if (hint == ForInHint::kNone) {
790 return InlineCacheState::UNINITIALIZED;
791 } else if (hint == ForInHint::kAny) {
792 return InlineCacheState::GENERIC;
793 }
794 return InlineCacheState::MONOMORPHIC;
795 }
796 case FeedbackSlotKind::kInstanceOf: {
797 if (feedback == UninitializedSentinel()) {
798 return InlineCacheState::UNINITIALIZED;
799 } else if (feedback == MegamorphicSentinel()) {
800 return InlineCacheState::MEGAMORPHIC;
801 }
802 return InlineCacheState::MONOMORPHIC;
803 }
804 case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral: {
805 if (feedback == UninitializedSentinel()) {
806 return InlineCacheState::UNINITIALIZED;
807 } else if (feedback->IsWeakOrCleared()) {
808 // Don't check if the map is cleared.
809 return InlineCacheState::MONOMORPHIC;
810 }
811
812 return InlineCacheState::MEGAMORPHIC;
813 }
814 case FeedbackSlotKind::kTypeProfile: {
815 if (feedback == UninitializedSentinel()) {
816 return InlineCacheState::UNINITIALIZED;
817 }
818 return InlineCacheState::MONOMORPHIC;
819 }
820
821 case FeedbackSlotKind::kCloneObject: {
822 if (feedback == UninitializedSentinel()) {
823 return InlineCacheState::UNINITIALIZED;
824 }
825 if (feedback == MegamorphicSentinel()) {
826 return InlineCacheState::MEGAMORPHIC;
827 }
828 if (feedback->IsWeakOrCleared()) {
829 return InlineCacheState::MONOMORPHIC;
830 }
831
832 DCHECK(feedback->GetHeapObjectAssumeStrong().IsWeakFixedArray());
833 return InlineCacheState::POLYMORPHIC;
834 }
835
836 case FeedbackSlotKind::kInvalid:
837 case FeedbackSlotKind::kKindsNumber:
838 UNREACHABLE();
839 }
840 return InlineCacheState::UNINITIALIZED;
841 }
842
ConfigurePropertyCellMode(Handle<PropertyCell> cell)843 void FeedbackNexus::ConfigurePropertyCellMode(Handle<PropertyCell> cell) {
844 DCHECK(IsGlobalICKind(kind()));
845 SetFeedback(HeapObjectReference::Weak(*cell), UPDATE_WRITE_BARRIER,
846 UninitializedSentinel(), SKIP_WRITE_BARRIER);
847 }
848
ConfigureLexicalVarMode(int script_context_index,int context_slot_index,bool immutable)849 bool FeedbackNexus::ConfigureLexicalVarMode(int script_context_index,
850 int context_slot_index,
851 bool immutable) {
852 DCHECK(IsGlobalICKind(kind()));
853 DCHECK_LE(0, script_context_index);
854 DCHECK_LE(0, context_slot_index);
855 if (!ContextIndexBits::is_valid(script_context_index) ||
856 !SlotIndexBits::is_valid(context_slot_index) ||
857 !ImmutabilityBit::is_valid(immutable)) {
858 return false;
859 }
860 int config = ContextIndexBits::encode(script_context_index) |
861 SlotIndexBits::encode(context_slot_index) |
862 ImmutabilityBit::encode(immutable);
863
864 SetFeedback(Smi::From31BitPattern(config), SKIP_WRITE_BARRIER,
865 UninitializedSentinel(), SKIP_WRITE_BARRIER);
866 return true;
867 }
868
ConfigureHandlerMode(const MaybeObjectHandle & handler)869 void FeedbackNexus::ConfigureHandlerMode(const MaybeObjectHandle& handler) {
870 DCHECK(IsGlobalICKind(kind()));
871 DCHECK(IC::IsHandler(*handler));
872 SetFeedback(HeapObjectReference::ClearedValue(GetIsolate()),
873 UPDATE_WRITE_BARRIER, *handler, UPDATE_WRITE_BARRIER);
874 }
875
ConfigureCloneObject(Handle<Map> source_map,Handle<Map> result_map)876 void FeedbackNexus::ConfigureCloneObject(Handle<Map> source_map,
877 Handle<Map> result_map) {
878 DCHECK(config()->can_write());
879 Isolate* isolate = GetIsolate();
880 Handle<HeapObject> feedback;
881 {
882 MaybeObject maybe_feedback = GetFeedback();
883 if (maybe_feedback->IsStrongOrWeak()) {
884 feedback = handle(maybe_feedback->GetHeapObject(), isolate);
885 } else {
886 DCHECK(maybe_feedback->IsCleared());
887 }
888 }
889 switch (ic_state()) {
890 case InlineCacheState::UNINITIALIZED:
891 // Cache the first map seen which meets the fast case requirements.
892 SetFeedback(HeapObjectReference::Weak(*source_map), UPDATE_WRITE_BARRIER,
893 *result_map);
894 break;
895 case InlineCacheState::MONOMORPHIC:
896 if (feedback.is_null() || feedback.is_identical_to(source_map) ||
897 Map::cast(*feedback).is_deprecated()) {
898 SetFeedback(HeapObjectReference::Weak(*source_map),
899 UPDATE_WRITE_BARRIER, *result_map);
900 } else {
901 // Transition to POLYMORPHIC.
902 Handle<WeakFixedArray> array =
903 CreateArrayOfSize(2 * kCloneObjectPolymorphicEntrySize);
904 array->Set(0, HeapObjectReference::Weak(*feedback));
905 array->Set(1, GetFeedbackExtra());
906 array->Set(2, HeapObjectReference::Weak(*source_map));
907 array->Set(3, MaybeObject::FromObject(*result_map));
908 SetFeedback(*array, UPDATE_WRITE_BARRIER,
909 HeapObjectReference::ClearedValue(isolate));
910 }
911 break;
912 case InlineCacheState::POLYMORPHIC: {
913 const int kMaxElements = FLAG_max_valid_polymorphic_map_count *
914 kCloneObjectPolymorphicEntrySize;
915 Handle<WeakFixedArray> array = Handle<WeakFixedArray>::cast(feedback);
916 int i = 0;
917 for (; i < array->length(); i += kCloneObjectPolymorphicEntrySize) {
918 MaybeObject feedback_map = array->Get(i);
919 if (feedback_map->IsCleared()) break;
920 Handle<Map> cached_map(Map::cast(feedback_map->GetHeapObject()),
921 isolate);
922 if (cached_map.is_identical_to(source_map) ||
923 cached_map->is_deprecated())
924 break;
925 }
926
927 if (i >= array->length()) {
928 if (i == kMaxElements) {
929 // Transition to MEGAMORPHIC.
930 MaybeObject sentinel = MegamorphicSentinel();
931 SetFeedback(sentinel, SKIP_WRITE_BARRIER,
932 HeapObjectReference::ClearedValue(isolate));
933 break;
934 }
935
936 // Grow polymorphic feedback array.
937 Handle<WeakFixedArray> new_array = CreateArrayOfSize(
938 array->length() + kCloneObjectPolymorphicEntrySize);
939 for (int j = 0; j < array->length(); ++j) {
940 new_array->Set(j, array->Get(j));
941 }
942 SetFeedback(*new_array);
943 array = new_array;
944 }
945
946 array->Set(i, HeapObjectReference::Weak(*source_map));
947 array->Set(i + 1, MaybeObject::FromObject(*result_map));
948 break;
949 }
950
951 default:
952 UNREACHABLE();
953 }
954 }
955
GetCallCount()956 int FeedbackNexus::GetCallCount() {
957 DCHECK(IsCallICKind(kind()));
958
959 Object call_count = GetFeedbackExtra()->cast<Object>();
960 CHECK(call_count.IsSmi());
961 uint32_t value = static_cast<uint32_t>(Smi::ToInt(call_count));
962 return CallCountField::decode(value);
963 }
964
SetSpeculationMode(SpeculationMode mode)965 void FeedbackNexus::SetSpeculationMode(SpeculationMode mode) {
966 DCHECK(IsCallICKind(kind()));
967
968 Object call_count = GetFeedbackExtra()->cast<Object>();
969 CHECK(call_count.IsSmi());
970 uint32_t count = static_cast<uint32_t>(Smi::ToInt(call_count));
971 count = SpeculationModeField::update(count, mode);
972 MaybeObject feedback = GetFeedback();
973 // We could've skipped WB here (since we set the slot to the same value again)
974 // but we don't to make WB verification happy.
975 SetFeedback(feedback, UPDATE_WRITE_BARRIER, Smi::FromInt(count),
976 SKIP_WRITE_BARRIER);
977 }
978
GetSpeculationMode()979 SpeculationMode FeedbackNexus::GetSpeculationMode() {
980 DCHECK(IsCallICKind(kind()));
981
982 Object call_count = GetFeedbackExtra()->cast<Object>();
983 CHECK(call_count.IsSmi());
984 uint32_t value = static_cast<uint32_t>(Smi::ToInt(call_count));
985 return SpeculationModeField::decode(value);
986 }
987
GetCallFeedbackContent()988 CallFeedbackContent FeedbackNexus::GetCallFeedbackContent() {
989 DCHECK(IsCallICKind(kind()));
990
991 Object call_count = GetFeedbackExtra()->cast<Object>();
992 CHECK(call_count.IsSmi());
993 uint32_t value = static_cast<uint32_t>(Smi::ToInt(call_count));
994 return CallFeedbackContentField::decode(value);
995 }
996
ComputeCallFrequency()997 float FeedbackNexus::ComputeCallFrequency() {
998 DCHECK(IsCallICKind(kind()));
999
1000 double const invocation_count = vector().invocation_count(kRelaxedLoad);
1001 double const call_count = GetCallCount();
1002 if (invocation_count == 0.0) { // Prevent division by 0.
1003 return 0.0f;
1004 }
1005 return static_cast<float>(call_count / invocation_count);
1006 }
1007
ConfigureMonomorphic(Handle<Name> name,Handle<Map> receiver_map,const MaybeObjectHandle & handler)1008 void FeedbackNexus::ConfigureMonomorphic(Handle<Name> name,
1009 Handle<Map> receiver_map,
1010 const MaybeObjectHandle& handler) {
1011 DCHECK(handler.is_null() || IC::IsHandler(*handler));
1012 if (kind() == FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral) {
1013 SetFeedback(HeapObjectReference::Weak(*receiver_map), UPDATE_WRITE_BARRIER,
1014 *name);
1015 } else {
1016 if (name.is_null()) {
1017 SetFeedback(HeapObjectReference::Weak(*receiver_map),
1018 UPDATE_WRITE_BARRIER, *handler);
1019 } else {
1020 Handle<WeakFixedArray> array = CreateArrayOfSize(2);
1021 array->Set(0, HeapObjectReference::Weak(*receiver_map));
1022 array->Set(1, *handler);
1023 SetFeedback(*name, UPDATE_WRITE_BARRIER, *array);
1024 }
1025 }
1026 }
1027
ConfigurePolymorphic(Handle<Name> name,std::vector<MapAndHandler> const & maps_and_handlers)1028 void FeedbackNexus::ConfigurePolymorphic(
1029 Handle<Name> name, std::vector<MapAndHandler> const& maps_and_handlers) {
1030 int receiver_count = static_cast<int>(maps_and_handlers.size());
1031 DCHECK_GT(receiver_count, 1);
1032 Handle<WeakFixedArray> array = CreateArrayOfSize(receiver_count * 2);
1033
1034 for (int current = 0; current < receiver_count; ++current) {
1035 Handle<Map> map = maps_and_handlers[current].first;
1036 array->Set(current * 2, HeapObjectReference::Weak(*map));
1037 MaybeObjectHandle handler = maps_and_handlers[current].second;
1038 DCHECK(IC::IsHandler(*handler));
1039 array->Set(current * 2 + 1, *handler);
1040 }
1041
1042 if (name.is_null()) {
1043 SetFeedback(*array, UPDATE_WRITE_BARRIER, UninitializedSentinel(),
1044 SKIP_WRITE_BARRIER);
1045 } else {
1046 SetFeedback(*name, UPDATE_WRITE_BARRIER, *array);
1047 }
1048 }
1049
ExtractMaps(MapHandles * maps) const1050 int FeedbackNexus::ExtractMaps(MapHandles* maps) const {
1051 DisallowGarbageCollection no_gc;
1052 int found = 0;
1053 for (FeedbackIterator it(this); !it.done(); it.Advance()) {
1054 maps->push_back(config()->NewHandle(it.map()));
1055 found++;
1056 }
1057
1058 return found;
1059 }
1060
ExtractMapsAndFeedback(std::vector<MapAndFeedback> * maps_and_feedback) const1061 int FeedbackNexus::ExtractMapsAndFeedback(
1062 std::vector<MapAndFeedback>* maps_and_feedback) const {
1063 DisallowGarbageCollection no_gc;
1064 int found = 0;
1065
1066 for (FeedbackIterator it(this); !it.done(); it.Advance()) {
1067 Handle<Map> map = config()->NewHandle(it.map());
1068 MaybeObject maybe_handler = it.handler();
1069 if (!maybe_handler->IsCleared()) {
1070 DCHECK(IC::IsHandler(maybe_handler) ||
1071 IsDefineKeyedOwnPropertyInLiteralKind(kind()));
1072 MaybeObjectHandle handler = config()->NewHandle(maybe_handler);
1073 maps_and_feedback->push_back(MapAndHandler(map, handler));
1074 found++;
1075 }
1076 }
1077
1078 return found;
1079 }
1080
ExtractMapsAndHandlers(std::vector<MapAndHandler> * maps_and_handlers,TryUpdateHandler map_handler) const1081 int FeedbackNexus::ExtractMapsAndHandlers(
1082 std::vector<MapAndHandler>* maps_and_handlers,
1083 TryUpdateHandler map_handler) const {
1084 DCHECK(!IsDefineKeyedOwnPropertyInLiteralKind(kind()));
1085 DisallowGarbageCollection no_gc;
1086 int found = 0;
1087
1088 for (FeedbackIterator it(this); !it.done(); it.Advance()) {
1089 Handle<Map> map = config()->NewHandle(it.map());
1090 MaybeObject maybe_handler = it.handler();
1091 if (!maybe_handler->IsCleared()) {
1092 DCHECK(IC::IsHandler(maybe_handler));
1093 MaybeObjectHandle handler = config()->NewHandle(maybe_handler);
1094 if (map_handler && !(map_handler(map).ToHandle(&map))) {
1095 continue;
1096 }
1097 maps_and_handlers->push_back(MapAndHandler(map, handler));
1098 found++;
1099 }
1100 }
1101
1102 return found;
1103 }
1104
FindHandlerForMap(Handle<Map> map) const1105 MaybeObjectHandle FeedbackNexus::FindHandlerForMap(Handle<Map> map) const {
1106 DCHECK(!IsStoreInArrayLiteralICKind(kind()));
1107
1108 for (FeedbackIterator it(this); !it.done(); it.Advance()) {
1109 if (it.map() == *map && !it.handler()->IsCleared()) {
1110 return config()->NewHandle(it.handler());
1111 }
1112 }
1113 return MaybeObjectHandle();
1114 }
1115
GetName() const1116 Name FeedbackNexus::GetName() const {
1117 if (IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
1118 IsKeyedHasICKind(kind()) || IsDefineKeyedOwnICKind(kind())) {
1119 MaybeObject feedback = GetFeedback();
1120 if (IsPropertyNameFeedback(feedback)) {
1121 return Name::cast(feedback->GetHeapObjectAssumeStrong());
1122 }
1123 }
1124 if (IsDefineKeyedOwnPropertyInLiteralKind(kind())) {
1125 MaybeObject extra = GetFeedbackExtra();
1126 if (IsPropertyNameFeedback(extra)) {
1127 return Name::cast(extra->GetHeapObjectAssumeStrong());
1128 }
1129 }
1130 return Name();
1131 }
1132
GetKeyedAccessLoadMode() const1133 KeyedAccessLoadMode FeedbackNexus::GetKeyedAccessLoadMode() const {
1134 DCHECK(IsKeyedLoadICKind(kind()) || IsKeyedHasICKind(kind()));
1135
1136 if (GetKeyType() == IcCheckType::kProperty) return STANDARD_LOAD;
1137
1138 std::vector<MapAndHandler> maps_and_handlers;
1139 ExtractMapsAndHandlers(&maps_and_handlers);
1140 for (MapAndHandler map_and_handler : maps_and_handlers) {
1141 KeyedAccessLoadMode mode =
1142 LoadHandler::GetKeyedAccessLoadMode(*map_and_handler.second);
1143 if (mode != STANDARD_LOAD) return mode;
1144 }
1145
1146 return STANDARD_LOAD;
1147 }
1148
1149 namespace {
1150
BuiltinHasKeyedAccessStoreMode(Builtin builtin)1151 bool BuiltinHasKeyedAccessStoreMode(Builtin builtin) {
1152 DCHECK(Builtins::IsBuiltinId(builtin));
1153 switch (builtin) {
1154 case Builtin::kKeyedStoreIC_SloppyArguments_Standard:
1155 case Builtin::kKeyedStoreIC_SloppyArguments_GrowNoTransitionHandleCOW:
1156 case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionIgnoreOOB:
1157 case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionHandleCOW:
1158 case Builtin::kStoreFastElementIC_Standard:
1159 case Builtin::kStoreFastElementIC_GrowNoTransitionHandleCOW:
1160 case Builtin::kStoreFastElementIC_NoTransitionIgnoreOOB:
1161 case Builtin::kStoreFastElementIC_NoTransitionHandleCOW:
1162 case Builtin::kElementsTransitionAndStore_Standard:
1163 case Builtin::kElementsTransitionAndStore_GrowNoTransitionHandleCOW:
1164 case Builtin::kElementsTransitionAndStore_NoTransitionIgnoreOOB:
1165 case Builtin::kElementsTransitionAndStore_NoTransitionHandleCOW:
1166 return true;
1167 default:
1168 return false;
1169 }
1170 UNREACHABLE();
1171 }
1172
KeyedAccessStoreModeForBuiltin(Builtin builtin)1173 KeyedAccessStoreMode KeyedAccessStoreModeForBuiltin(Builtin builtin) {
1174 DCHECK(BuiltinHasKeyedAccessStoreMode(builtin));
1175 switch (builtin) {
1176 case Builtin::kKeyedStoreIC_SloppyArguments_Standard:
1177 case Builtin::kStoreFastElementIC_Standard:
1178 case Builtin::kElementsTransitionAndStore_Standard:
1179 return STANDARD_STORE;
1180 case Builtin::kKeyedStoreIC_SloppyArguments_GrowNoTransitionHandleCOW:
1181 case Builtin::kStoreFastElementIC_GrowNoTransitionHandleCOW:
1182 case Builtin::kElementsTransitionAndStore_GrowNoTransitionHandleCOW:
1183 return STORE_AND_GROW_HANDLE_COW;
1184 case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionIgnoreOOB:
1185 case Builtin::kStoreFastElementIC_NoTransitionIgnoreOOB:
1186 case Builtin::kElementsTransitionAndStore_NoTransitionIgnoreOOB:
1187 return STORE_IGNORE_OUT_OF_BOUNDS;
1188 case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionHandleCOW:
1189 case Builtin::kStoreFastElementIC_NoTransitionHandleCOW:
1190 case Builtin::kElementsTransitionAndStore_NoTransitionHandleCOW:
1191 return STORE_HANDLE_COW;
1192 default:
1193 UNREACHABLE();
1194 }
1195 }
1196
1197 } // namespace
1198
GetKeyedAccessStoreMode() const1199 KeyedAccessStoreMode FeedbackNexus::GetKeyedAccessStoreMode() const {
1200 DCHECK(IsKeyedStoreICKind(kind()) || IsStoreInArrayLiteralICKind(kind()) ||
1201 IsDefineKeyedOwnPropertyInLiteralKind(kind()) ||
1202 IsDefineKeyedOwnICKind(kind()));
1203 KeyedAccessStoreMode mode = STANDARD_STORE;
1204
1205 if (GetKeyType() == IcCheckType::kProperty) return mode;
1206
1207 std::vector<MapAndHandler> maps_and_handlers;
1208 ExtractMapsAndHandlers(&maps_and_handlers);
1209 for (const MapAndHandler& map_and_handler : maps_and_handlers) {
1210 const MaybeObjectHandle maybe_code_handler = map_and_handler.second;
1211 // The first handler that isn't the slow handler will have the bits we need.
1212 Handle<Code> handler;
1213 if (maybe_code_handler.object()->IsStoreHandler()) {
1214 Handle<StoreHandler> data_handler =
1215 Handle<StoreHandler>::cast(maybe_code_handler.object());
1216
1217 if ((data_handler->smi_handler()).IsSmi()) {
1218 // Decode the KeyedAccessStoreMode information from the Handler.
1219 mode = StoreHandler::GetKeyedAccessStoreMode(
1220 MaybeObject::FromObject(data_handler->smi_handler()));
1221 if (mode != STANDARD_STORE) return mode;
1222 continue;
1223 } else {
1224 Code code = FromCodeT(CodeT::cast(data_handler->smi_handler()));
1225 handler = config()->NewHandle(code);
1226 }
1227
1228 } else if (maybe_code_handler.object()->IsSmi()) {
1229 // Skip for Proxy Handlers.
1230 if (*maybe_code_handler.object() == StoreHandler::StoreProxy()) {
1231 continue;
1232 }
1233 // Decode the KeyedAccessStoreMode information from the Handler.
1234 mode = StoreHandler::GetKeyedAccessStoreMode(*maybe_code_handler);
1235 if (mode != STANDARD_STORE) return mode;
1236 continue;
1237 } else if (IsDefineKeyedOwnICKind(kind())) {
1238 mode = StoreHandler::GetKeyedAccessStoreMode(*maybe_code_handler);
1239 if (mode != STANDARD_STORE) return mode;
1240 continue;
1241 } else {
1242 // Element store without prototype chain check.
1243 if (V8_EXTERNAL_CODE_SPACE_BOOL) {
1244 Code code = FromCodeT(CodeT::cast(*maybe_code_handler.object()));
1245 handler = config()->NewHandle(code);
1246 } else {
1247 handler = Handle<Code>::cast(maybe_code_handler.object());
1248 }
1249 }
1250
1251 if (handler->is_builtin()) {
1252 Builtin builtin = handler->builtin_id();
1253 if (!BuiltinHasKeyedAccessStoreMode(builtin)) continue;
1254
1255 mode = KeyedAccessStoreModeForBuiltin(builtin);
1256 break;
1257 }
1258 }
1259
1260 return mode;
1261 }
1262
GetKeyType() const1263 IcCheckType FeedbackNexus::GetKeyType() const {
1264 DCHECK(IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
1265 IsStoreInArrayLiteralICKind(kind()) || IsKeyedHasICKind(kind()) ||
1266 IsDefineKeyedOwnPropertyInLiteralKind(kind()) ||
1267 IsDefineKeyedOwnICKind(kind()));
1268 auto pair = GetFeedbackPair();
1269 MaybeObject feedback = pair.first;
1270 if (feedback == MegamorphicSentinel()) {
1271 return static_cast<IcCheckType>(
1272 Smi::ToInt(pair.second->template cast<Object>()));
1273 }
1274 MaybeObject maybe_name = IsDefineKeyedOwnPropertyInLiteralKind(kind()) ||
1275 IsDefineKeyedOwnICKind(kind())
1276 ? pair.second
1277 : feedback;
1278 return IsPropertyNameFeedback(maybe_name) ? IcCheckType::kProperty
1279 : IcCheckType::kElement;
1280 }
1281
GetBinaryOperationFeedback() const1282 BinaryOperationHint FeedbackNexus::GetBinaryOperationFeedback() const {
1283 DCHECK_EQ(kind(), FeedbackSlotKind::kBinaryOp);
1284 int feedback = GetFeedback().ToSmi().value();
1285 return BinaryOperationHintFromFeedback(feedback);
1286 }
1287
GetCompareOperationFeedback() const1288 CompareOperationHint FeedbackNexus::GetCompareOperationFeedback() const {
1289 DCHECK_EQ(kind(), FeedbackSlotKind::kCompareOp);
1290 int feedback = GetFeedback().ToSmi().value();
1291 return CompareOperationHintFromFeedback(feedback);
1292 }
1293
GetForInFeedback() const1294 ForInHint FeedbackNexus::GetForInFeedback() const {
1295 DCHECK_EQ(kind(), FeedbackSlotKind::kForIn);
1296 int feedback = GetFeedback().ToSmi().value();
1297 return ForInHintFromFeedback(static_cast<ForInFeedback>(feedback));
1298 }
1299
GetConstructorFeedback() const1300 MaybeHandle<JSObject> FeedbackNexus::GetConstructorFeedback() const {
1301 DCHECK_EQ(kind(), FeedbackSlotKind::kInstanceOf);
1302 MaybeObject feedback = GetFeedback();
1303 HeapObject heap_object;
1304 if (feedback->GetHeapObjectIfWeak(&heap_object)) {
1305 return config()->NewHandle(JSObject::cast(heap_object));
1306 }
1307 return MaybeHandle<JSObject>();
1308 }
1309
1310 namespace {
1311
InList(Handle<ArrayList> types,Handle<String> type)1312 bool InList(Handle<ArrayList> types, Handle<String> type) {
1313 for (int i = 0; i < types->Length(); i++) {
1314 Object obj = types->Get(i);
1315 if (String::cast(obj).Equals(*type)) {
1316 return true;
1317 }
1318 }
1319 return false;
1320 }
1321 } // anonymous namespace
1322
Collect(Handle<String> type,int position)1323 void FeedbackNexus::Collect(Handle<String> type, int position) {
1324 DCHECK(IsTypeProfileKind(kind()));
1325 DCHECK_GE(position, 0);
1326 DCHECK(config()->can_write());
1327 Isolate* isolate = GetIsolate();
1328
1329 MaybeObject const feedback = GetFeedback();
1330
1331 // Map source position to collection of types
1332 Handle<SimpleNumberDictionary> types;
1333
1334 if (feedback == UninitializedSentinel()) {
1335 types = SimpleNumberDictionary::New(isolate, 1);
1336 } else {
1337 types = handle(
1338 SimpleNumberDictionary::cast(feedback->GetHeapObjectAssumeStrong()),
1339 isolate);
1340 }
1341
1342 Handle<ArrayList> position_specific_types;
1343
1344 InternalIndex entry = types->FindEntry(isolate, position);
1345 if (entry.is_not_found()) {
1346 position_specific_types = ArrayList::New(isolate, 1);
1347 types = SimpleNumberDictionary::Set(
1348 isolate, types, position,
1349 ArrayList::Add(isolate, position_specific_types, type));
1350 } else {
1351 DCHECK(types->ValueAt(entry).IsArrayList());
1352 position_specific_types =
1353 handle(ArrayList::cast(types->ValueAt(entry)), isolate);
1354 if (!InList(position_specific_types, type)) { // Add type
1355 types = SimpleNumberDictionary::Set(
1356 isolate, types, position,
1357 ArrayList::Add(isolate, position_specific_types, type));
1358 }
1359 }
1360 SetFeedback(*types);
1361 }
1362
GetSourcePositions() const1363 std::vector<int> FeedbackNexus::GetSourcePositions() const {
1364 DCHECK(IsTypeProfileKind(kind()));
1365 std::vector<int> source_positions;
1366 Isolate* isolate = GetIsolate();
1367
1368 MaybeObject const feedback = GetFeedback();
1369
1370 if (feedback == UninitializedSentinel()) {
1371 return source_positions;
1372 }
1373
1374 Handle<SimpleNumberDictionary> types(
1375 SimpleNumberDictionary::cast(feedback->GetHeapObjectAssumeStrong()),
1376 isolate);
1377
1378 for (int index = SimpleNumberDictionary::kElementsStartIndex;
1379 index < types->length(); index += SimpleNumberDictionary::kEntrySize) {
1380 int key_index = index + SimpleNumberDictionary::kEntryKeyIndex;
1381 Object key = types->get(key_index);
1382 if (key.IsSmi()) {
1383 int position = Smi::cast(key).value();
1384 source_positions.push_back(position);
1385 }
1386 }
1387 return source_positions;
1388 }
1389
GetTypesForSourcePositions(uint32_t position) const1390 std::vector<Handle<String>> FeedbackNexus::GetTypesForSourcePositions(
1391 uint32_t position) const {
1392 DCHECK(IsTypeProfileKind(kind()));
1393 Isolate* isolate = GetIsolate();
1394
1395 MaybeObject const feedback = GetFeedback();
1396 std::vector<Handle<String>> types_for_position;
1397 if (feedback == UninitializedSentinel()) {
1398 return types_for_position;
1399 }
1400
1401 Handle<SimpleNumberDictionary> types(
1402 SimpleNumberDictionary::cast(feedback->GetHeapObjectAssumeStrong()),
1403 isolate);
1404
1405 InternalIndex entry = types->FindEntry(isolate, position);
1406 if (entry.is_not_found()) return types_for_position;
1407
1408 DCHECK(types->ValueAt(entry).IsArrayList());
1409 Handle<ArrayList> position_specific_types =
1410 Handle<ArrayList>(ArrayList::cast(types->ValueAt(entry)), isolate);
1411 for (int i = 0; i < position_specific_types->Length(); i++) {
1412 Object t = position_specific_types->Get(i);
1413 types_for_position.push_back(Handle<String>(String::cast(t), isolate));
1414 }
1415
1416 return types_for_position;
1417 }
1418
ResetTypeProfile()1419 void FeedbackNexus::ResetTypeProfile() {
1420 DCHECK(IsTypeProfileKind(kind()));
1421 SetFeedback(UninitializedSentinel());
1422 }
1423
FeedbackIterator(const FeedbackNexus * nexus)1424 FeedbackIterator::FeedbackIterator(const FeedbackNexus* nexus)
1425 : done_(false), index_(-1), state_(kOther) {
1426 DCHECK(
1427 IsLoadICKind(nexus->kind()) || IsSetNamedICKind(nexus->kind()) ||
1428 IsKeyedLoadICKind(nexus->kind()) || IsKeyedStoreICKind(nexus->kind()) ||
1429 IsDefineNamedOwnICKind(nexus->kind()) ||
1430 IsDefineKeyedOwnPropertyInLiteralKind(nexus->kind()) ||
1431 IsStoreInArrayLiteralICKind(nexus->kind()) ||
1432 IsKeyedHasICKind(nexus->kind()) || IsDefineKeyedOwnICKind(nexus->kind()));
1433
1434 DisallowGarbageCollection no_gc;
1435 auto pair = nexus->GetFeedbackPair();
1436 MaybeObject feedback = pair.first;
1437 bool is_named_feedback = IsPropertyNameFeedback(feedback);
1438 HeapObject heap_object;
1439
1440 if ((feedback->GetHeapObjectIfStrong(&heap_object) &&
1441 heap_object.IsWeakFixedArray()) ||
1442 is_named_feedback) {
1443 index_ = 0;
1444 state_ = kPolymorphic;
1445 heap_object = feedback->GetHeapObjectAssumeStrong();
1446 if (is_named_feedback) {
1447 polymorphic_feedback_ = nexus->config()->NewHandle(
1448 WeakFixedArray::cast(pair.second->GetHeapObjectAssumeStrong()));
1449 } else {
1450 polymorphic_feedback_ =
1451 nexus->config()->NewHandle(WeakFixedArray::cast(heap_object));
1452 }
1453 AdvancePolymorphic();
1454 } else if (feedback->GetHeapObjectIfWeak(&heap_object)) {
1455 state_ = kMonomorphic;
1456 MaybeObject handler = pair.second;
1457 map_ = Map::cast(heap_object);
1458 handler_ = handler;
1459 } else {
1460 done_ = true;
1461 }
1462 }
1463
Advance()1464 void FeedbackIterator::Advance() {
1465 CHECK(!done_);
1466
1467 if (state_ == kMonomorphic) {
1468 done_ = true;
1469 return;
1470 }
1471
1472 CHECK_EQ(state_, kPolymorphic);
1473 AdvancePolymorphic();
1474 }
1475
AdvancePolymorphic()1476 void FeedbackIterator::AdvancePolymorphic() {
1477 CHECK(!done_);
1478 CHECK_EQ(state_, kPolymorphic);
1479 int length = polymorphic_feedback_->length();
1480 HeapObject heap_object;
1481
1482 while (index_ < length) {
1483 if (polymorphic_feedback_->Get(index_)->GetHeapObjectIfWeak(&heap_object)) {
1484 MaybeObject handler = polymorphic_feedback_->Get(index_ + kHandlerOffset);
1485 map_ = Map::cast(heap_object);
1486 handler_ = handler;
1487 index_ += kEntrySize;
1488 return;
1489 }
1490 index_ += kEntrySize;
1491 }
1492
1493 CHECK_EQ(index_, length);
1494 done_ = true;
1495 }
1496 } // namespace internal
1497 } // namespace v8
1498