• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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/compiler/js-heap-broker.h"
6 
7 #ifdef ENABLE_SLOW_DCHECKS
8 #include <algorithm>
9 #endif
10 
11 #include "src/codegen/code-factory.h"
12 #include "src/codegen/optimized-compilation-info.h"
13 #include "src/handles/handles-inl.h"
14 #include "src/heap/heap-inl.h"
15 #include "src/ic/handler-configuration-inl.h"
16 #include "src/init/bootstrapper.h"
17 #include "src/objects/allocation-site-inl.h"
18 #include "src/objects/data-handler-inl.h"
19 #include "src/objects/feedback-cell.h"
20 #include "src/objects/js-array-inl.h"
21 #include "src/objects/literal-objects-inl.h"
22 #include "src/objects/map-updater.h"
23 #include "src/objects/objects-inl.h"
24 #include "src/objects/oddball.h"
25 #include "src/objects/property-cell.h"
26 
27 namespace v8 {
28 namespace internal {
29 namespace compiler {
30 
31 #define TRACE(broker, x) TRACE_BROKER(broker, x)
32 
33 #ifdef V8_STATIC_CONSTEXPR_VARIABLES_NEED_DEFINITIONS
34 // These definitions are here in order to please the linker, which in debug mode
35 // sometimes requires static constants to be defined in .cc files.
36 // This is, however, deprecated (and unnecessary) in C++17.
37 const uint32_t JSHeapBroker::kMinimalRefsBucketCount;
38 const uint32_t JSHeapBroker::kInitialRefsBucketCount;
39 #endif
40 
IncrementTracingIndentation()41 void JSHeapBroker::IncrementTracingIndentation() { ++trace_indentation_; }
42 
DecrementTracingIndentation()43 void JSHeapBroker::DecrementTracingIndentation() { --trace_indentation_; }
44 
JSHeapBroker(Isolate * isolate,Zone * broker_zone,bool tracing_enabled,CodeKind code_kind)45 JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone,
46                            bool tracing_enabled, CodeKind code_kind)
47     : isolate_(isolate),
48 #if V8_COMPRESS_POINTERS
49       cage_base_(isolate),
50 #endif  // V8_COMPRESS_POINTERS
51       zone_(broker_zone),
52       // Note that this initialization of {refs_} with the minimal initial
53       // capacity is redundant in the normal use case (concurrent compilation
54       // enabled, standard objects to be serialized), as the map is going to be
55       // replaced immediately with a larger-capacity one.  It doesn't seem to
56       // affect the performance in a noticeable way though.
57       refs_(zone()->New<RefsMap>(kMinimalRefsBucketCount, AddressMatcher(),
58                                  zone())),
59       root_index_map_(isolate),
60       array_and_object_prototypes_(zone()),
61       tracing_enabled_(tracing_enabled),
62       code_kind_(code_kind),
63       feedback_(zone()),
64       property_access_infos_(zone()) {
65   TRACE(this, "Constructing heap broker");
66 }
67 
~JSHeapBroker()68 JSHeapBroker::~JSHeapBroker() { DCHECK_NULL(local_isolate_); }
69 
SetPersistentAndCopyCanonicalHandlesForTesting(std::unique_ptr<PersistentHandles> persistent_handles,std::unique_ptr<CanonicalHandlesMap> canonical_handles)70 void JSHeapBroker::SetPersistentAndCopyCanonicalHandlesForTesting(
71     std::unique_ptr<PersistentHandles> persistent_handles,
72     std::unique_ptr<CanonicalHandlesMap> canonical_handles) {
73   set_persistent_handles(std::move(persistent_handles));
74   CopyCanonicalHandlesForTesting(std::move(canonical_handles));
75 }
76 
CopyCanonicalHandlesForTesting(std::unique_ptr<CanonicalHandlesMap> canonical_handles)77 void JSHeapBroker::CopyCanonicalHandlesForTesting(
78     std::unique_ptr<CanonicalHandlesMap> canonical_handles) {
79   DCHECK_NULL(canonical_handles_);
80   canonical_handles_ = std::make_unique<CanonicalHandlesMap>(
81       isolate_->heap(), ZoneAllocationPolicy(zone()));
82 
83   CanonicalHandlesMap::IteratableScope it_scope(canonical_handles.get());
84   for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
85     Address* entry = *it.entry();
86     Object key = it.key();
87     canonical_handles_->Insert(key, entry);
88   }
89 }
90 
Trace() const91 std::string JSHeapBroker::Trace() const {
92   std::ostringstream oss;
93   oss << "[" << this << "] ";
94   for (unsigned i = 0; i < trace_indentation_ * 2; ++i) oss.put(' ');
95   return oss.str();
96 }
97 
AttachLocalIsolate(OptimizedCompilationInfo * info,LocalIsolate * local_isolate)98 void JSHeapBroker::AttachLocalIsolate(OptimizedCompilationInfo* info,
99                                       LocalIsolate* local_isolate) {
100   set_canonical_handles(info->DetachCanonicalHandles());
101   DCHECK_NULL(local_isolate_);
102   local_isolate_ = local_isolate;
103   DCHECK_NOT_NULL(local_isolate_);
104   local_isolate_->heap()->AttachPersistentHandles(
105       info->DetachPersistentHandles());
106 }
107 
DetachLocalIsolate(OptimizedCompilationInfo * info)108 void JSHeapBroker::DetachLocalIsolate(OptimizedCompilationInfo* info) {
109   DCHECK_NULL(ph_);
110   DCHECK_NOT_NULL(local_isolate_);
111   std::unique_ptr<PersistentHandles> ph =
112       local_isolate_->heap()->DetachPersistentHandles();
113   local_isolate_ = nullptr;
114   info->set_canonical_handles(DetachCanonicalHandles());
115   info->set_persistent_handles(std::move(ph));
116 }
117 
StopSerializing()118 void JSHeapBroker::StopSerializing() {
119   CHECK_EQ(mode_, kSerializing);
120   TRACE(this, "Stopping serialization");
121   mode_ = kSerialized;
122 }
123 
Retire()124 void JSHeapBroker::Retire() {
125   CHECK_EQ(mode_, kSerialized);
126   TRACE(this, "Retiring");
127   mode_ = kRetired;
128 }
129 
SetTargetNativeContextRef(Handle<NativeContext> native_context)130 void JSHeapBroker::SetTargetNativeContextRef(
131     Handle<NativeContext> native_context) {
132   DCHECK((mode() == kDisabled && !target_native_context_.has_value()) ||
133          (mode() == kSerializing &&
134           target_native_context_->object().is_identical_to(native_context)));
135   target_native_context_ = MakeRef(this, *native_context);
136 }
137 
CollectArrayAndObjectPrototypes()138 void JSHeapBroker::CollectArrayAndObjectPrototypes() {
139   DisallowGarbageCollection no_gc;
140   CHECK_EQ(mode(), kSerializing);
141   CHECK(array_and_object_prototypes_.empty());
142 
143   Object maybe_context = isolate()->heap()->native_contexts_list();
144   while (!maybe_context.IsUndefined(isolate())) {
145     Context context = Context::cast(maybe_context);
146     Object array_prot = context.get(Context::INITIAL_ARRAY_PROTOTYPE_INDEX);
147     Object object_prot = context.get(Context::INITIAL_OBJECT_PROTOTYPE_INDEX);
148     array_and_object_prototypes_.emplace(JSObject::cast(array_prot), isolate());
149     array_and_object_prototypes_.emplace(JSObject::cast(object_prot),
150                                          isolate());
151     maybe_context = context.next_context_link();
152   }
153 
154   CHECK(!array_and_object_prototypes_.empty());
155 }
156 
GetTypedArrayStringTag(ElementsKind kind)157 StringRef JSHeapBroker::GetTypedArrayStringTag(ElementsKind kind) {
158   DCHECK(IsTypedArrayElementsKind(kind));
159   switch (kind) {
160 #define TYPED_ARRAY_STRING_TAG(Type, type, TYPE, ctype) \
161   case ElementsKind::TYPE##_ELEMENTS:                   \
162     return MakeRef(this, isolate()->factory()->Type##Array_string());
163     TYPED_ARRAYS(TYPED_ARRAY_STRING_TAG)
164 #undef TYPED_ARRAY_STRING_TAG
165     default:
166       UNREACHABLE();
167   }
168 }
169 
IsArrayOrObjectPrototype(const JSObjectRef & object) const170 bool JSHeapBroker::IsArrayOrObjectPrototype(const JSObjectRef& object) const {
171   return IsArrayOrObjectPrototype(object.object());
172 }
173 
IsArrayOrObjectPrototype(Handle<JSObject> object) const174 bool JSHeapBroker::IsArrayOrObjectPrototype(Handle<JSObject> object) const {
175   if (mode() == kDisabled) {
176     return isolate()->IsInAnyContext(*object,
177                                      Context::INITIAL_ARRAY_PROTOTYPE_INDEX) ||
178            isolate()->IsInAnyContext(*object,
179                                      Context::INITIAL_OBJECT_PROTOTYPE_INDEX);
180   }
181   CHECK(!array_and_object_prototypes_.empty());
182   return array_and_object_prototypes_.find(object) !=
183          array_and_object_prototypes_.end();
184 }
185 
TryGetOrCreateData(Object object,GetOrCreateDataFlags flags)186 ObjectData* JSHeapBroker::TryGetOrCreateData(Object object,
187                                              GetOrCreateDataFlags flags) {
188   return TryGetOrCreateData(CanonicalPersistentHandle(object), flags);
189 }
190 
GetOrCreateData(Handle<Object> object,GetOrCreateDataFlags flags)191 ObjectData* JSHeapBroker::GetOrCreateData(Handle<Object> object,
192                                           GetOrCreateDataFlags flags) {
193   ObjectData* return_value = TryGetOrCreateData(object, flags | kCrashOnError);
194   DCHECK_NOT_NULL(return_value);
195   return return_value;
196 }
197 
GetOrCreateData(Object object,GetOrCreateDataFlags flags)198 ObjectData* JSHeapBroker::GetOrCreateData(Object object,
199                                           GetOrCreateDataFlags flags) {
200   return GetOrCreateData(CanonicalPersistentHandle(object), flags);
201 }
202 
StackHasOverflowed() const203 bool JSHeapBroker::StackHasOverflowed() const {
204   DCHECK_IMPLIES(local_isolate_ == nullptr,
205                  ThreadId::Current() == isolate_->thread_id());
206   return (local_isolate_ != nullptr)
207              ? StackLimitCheck::HasOverflowed(local_isolate_)
208              : StackLimitCheck(isolate_).HasOverflowed();
209 }
210 
ObjectMayBeUninitialized(Handle<Object> object) const211 bool JSHeapBroker::ObjectMayBeUninitialized(Handle<Object> object) const {
212   return ObjectMayBeUninitialized(*object);
213 }
214 
ObjectMayBeUninitialized(Object object) const215 bool JSHeapBroker::ObjectMayBeUninitialized(Object object) const {
216   if (!object.IsHeapObject()) return false;
217   return ObjectMayBeUninitialized(HeapObject::cast(object));
218 }
219 
ObjectMayBeUninitialized(HeapObject object) const220 bool JSHeapBroker::ObjectMayBeUninitialized(HeapObject object) const {
221   return !IsMainThread() && isolate()->heap()->IsPendingAllocation(object);
222 }
223 
ProcessedFeedback(Kind kind,FeedbackSlotKind slot_kind)224 ProcessedFeedback::ProcessedFeedback(Kind kind, FeedbackSlotKind slot_kind)
225     : kind_(kind), slot_kind_(slot_kind) {}
226 
keyed_mode() const227 KeyedAccessMode ElementAccessFeedback::keyed_mode() const {
228   return keyed_mode_;
229 }
230 
231 ZoneVector<ElementAccessFeedback::TransitionGroup> const&
transition_groups() const232 ElementAccessFeedback::transition_groups() const {
233   return transition_groups_;
234 }
235 
Refine(JSHeapBroker * broker,ZoneVector<MapRef> const & inferred_maps) const236 ElementAccessFeedback const& ElementAccessFeedback::Refine(
237     JSHeapBroker* broker, ZoneVector<MapRef> const& inferred_maps) const {
238   ElementAccessFeedback& refined_feedback =
239       *broker->zone()->New<ElementAccessFeedback>(broker->zone(), keyed_mode(),
240                                                   slot_kind());
241   if (inferred_maps.empty()) return refined_feedback;
242 
243   ZoneRefUnorderedSet<MapRef> inferred(broker->zone());
244   inferred.insert(inferred_maps.begin(), inferred_maps.end());
245 
246   for (auto const& group : transition_groups()) {
247     DCHECK(!group.empty());
248     TransitionGroup new_group(broker->zone());
249     for (size_t i = 1; i < group.size(); ++i) {
250       MapRef source = MakeRefAssumeMemoryFence(broker, *group[i]);
251       if (inferred.find(source) != inferred.end()) {
252         new_group.push_back(source.object());
253       }
254     }
255 
256     MapRef target = MakeRefAssumeMemoryFence(broker, *group.front());
257     bool const keep_target =
258         inferred.find(target) != inferred.end() || new_group.size() > 1;
259     if (keep_target) {
260       new_group.push_back(target.object());
261       // The target must be at the front, the order of sources doesn't matter.
262       std::swap(new_group[0], new_group[new_group.size() - 1]);
263     }
264 
265     if (!new_group.empty()) {
266       DCHECK(new_group.size() == 1 ||
267              new_group.front().equals(target.object()));
268       refined_feedback.transition_groups_.push_back(std::move(new_group));
269     }
270   }
271   return refined_feedback;
272 }
273 
InsufficientFeedback(FeedbackSlotKind slot_kind)274 InsufficientFeedback::InsufficientFeedback(FeedbackSlotKind slot_kind)
275     : ProcessedFeedback(kInsufficient, slot_kind) {}
276 
GlobalAccessFeedback(PropertyCellRef cell,FeedbackSlotKind slot_kind)277 GlobalAccessFeedback::GlobalAccessFeedback(PropertyCellRef cell,
278                                            FeedbackSlotKind slot_kind)
279     : ProcessedFeedback(kGlobalAccess, slot_kind),
280       cell_or_context_(cell),
281       index_and_immutable_(0 /* doesn't matter */) {
282   DCHECK(IsGlobalICKind(slot_kind));
283 }
284 
GlobalAccessFeedback(FeedbackSlotKind slot_kind)285 GlobalAccessFeedback::GlobalAccessFeedback(FeedbackSlotKind slot_kind)
286     : ProcessedFeedback(kGlobalAccess, slot_kind),
287       index_and_immutable_(0 /* doesn't matter */) {
288   DCHECK(IsGlobalICKind(slot_kind));
289 }
290 
GlobalAccessFeedback(ContextRef script_context,int slot_index,bool immutable,FeedbackSlotKind slot_kind)291 GlobalAccessFeedback::GlobalAccessFeedback(ContextRef script_context,
292                                            int slot_index, bool immutable,
293                                            FeedbackSlotKind slot_kind)
294     : ProcessedFeedback(kGlobalAccess, slot_kind),
295       cell_or_context_(script_context),
296       index_and_immutable_(FeedbackNexus::SlotIndexBits::encode(slot_index) |
297                            FeedbackNexus::ImmutabilityBit::encode(immutable)) {
298   DCHECK_EQ(this->slot_index(), slot_index);
299   DCHECK_EQ(this->immutable(), immutable);
300   DCHECK(IsGlobalICKind(slot_kind));
301 }
302 
IsMegamorphic() const303 bool GlobalAccessFeedback::IsMegamorphic() const {
304   return !cell_or_context_.has_value();
305 }
IsPropertyCell() const306 bool GlobalAccessFeedback::IsPropertyCell() const {
307   return cell_or_context_.has_value() && cell_or_context_->IsPropertyCell();
308 }
IsScriptContextSlot() const309 bool GlobalAccessFeedback::IsScriptContextSlot() const {
310   return cell_or_context_.has_value() && cell_or_context_->IsContext();
311 }
property_cell() const312 PropertyCellRef GlobalAccessFeedback::property_cell() const {
313   CHECK(IsPropertyCell());
314   return cell_or_context_->AsPropertyCell();
315 }
script_context() const316 ContextRef GlobalAccessFeedback::script_context() const {
317   CHECK(IsScriptContextSlot());
318   return cell_or_context_->AsContext();
319 }
slot_index() const320 int GlobalAccessFeedback::slot_index() const {
321   DCHECK(IsScriptContextSlot());
322   return FeedbackNexus::SlotIndexBits::decode(index_and_immutable_);
323 }
immutable() const324 bool GlobalAccessFeedback::immutable() const {
325   DCHECK(IsScriptContextSlot());
326   return FeedbackNexus::ImmutabilityBit::decode(index_and_immutable_);
327 }
328 
GetConstantHint() const329 base::Optional<ObjectRef> GlobalAccessFeedback::GetConstantHint() const {
330   if (IsPropertyCell()) {
331     bool cell_cached = property_cell().Cache();
332     CHECK(cell_cached);  // Can't fail on the main thread.
333     return property_cell().value();
334   } else if (IsScriptContextSlot() && immutable()) {
335     return script_context().get(slot_index());
336   } else {
337     return base::nullopt;
338   }
339 }
340 
FromNexus(FeedbackNexus const & nexus)341 KeyedAccessMode KeyedAccessMode::FromNexus(FeedbackNexus const& nexus) {
342   FeedbackSlotKind kind = nexus.kind();
343   if (IsKeyedLoadICKind(kind)) {
344     return KeyedAccessMode(AccessMode::kLoad, nexus.GetKeyedAccessLoadMode());
345   }
346   if (IsKeyedHasICKind(kind)) {
347     return KeyedAccessMode(AccessMode::kHas, nexus.GetKeyedAccessLoadMode());
348   }
349   if (IsDefineKeyedOwnICKind(kind)) {
350     return KeyedAccessMode(AccessMode::kDefine,
351                            nexus.GetKeyedAccessStoreMode());
352   }
353   if (IsKeyedStoreICKind(kind)) {
354     return KeyedAccessMode(AccessMode::kStore, nexus.GetKeyedAccessStoreMode());
355   }
356   if (IsStoreInArrayLiteralICKind(kind) ||
357       IsDefineKeyedOwnPropertyInLiteralKind(kind)) {
358     return KeyedAccessMode(AccessMode::kStoreInLiteral,
359                            nexus.GetKeyedAccessStoreMode());
360   }
361   UNREACHABLE();
362 }
363 
access_mode() const364 AccessMode KeyedAccessMode::access_mode() const { return access_mode_; }
365 
IsLoad() const366 bool KeyedAccessMode::IsLoad() const {
367   return access_mode_ == AccessMode::kLoad || access_mode_ == AccessMode::kHas;
368 }
IsStore() const369 bool KeyedAccessMode::IsStore() const {
370   return access_mode_ == AccessMode::kStore ||
371          access_mode_ == AccessMode::kDefine ||
372          access_mode_ == AccessMode::kStoreInLiteral;
373 }
374 
load_mode() const375 KeyedAccessLoadMode KeyedAccessMode::load_mode() const {
376   CHECK(IsLoad());
377   return load_store_mode_.load_mode;
378 }
379 
store_mode() const380 KeyedAccessStoreMode KeyedAccessMode::store_mode() const {
381   CHECK(IsStore());
382   return load_store_mode_.store_mode;
383 }
384 
LoadStoreMode(KeyedAccessLoadMode load_mode)385 KeyedAccessMode::LoadStoreMode::LoadStoreMode(KeyedAccessLoadMode load_mode)
386     : load_mode(load_mode) {}
LoadStoreMode(KeyedAccessStoreMode store_mode)387 KeyedAccessMode::LoadStoreMode::LoadStoreMode(KeyedAccessStoreMode store_mode)
388     : store_mode(store_mode) {}
389 
KeyedAccessMode(AccessMode access_mode,KeyedAccessLoadMode load_mode)390 KeyedAccessMode::KeyedAccessMode(AccessMode access_mode,
391                                  KeyedAccessLoadMode load_mode)
392     : access_mode_(access_mode), load_store_mode_(load_mode) {
393   CHECK(!IsStore());
394   CHECK(IsLoad());
395 }
KeyedAccessMode(AccessMode access_mode,KeyedAccessStoreMode store_mode)396 KeyedAccessMode::KeyedAccessMode(AccessMode access_mode,
397                                  KeyedAccessStoreMode store_mode)
398     : access_mode_(access_mode), load_store_mode_(store_mode) {
399   CHECK(!IsLoad());
400   CHECK(IsStore());
401 }
402 
ElementAccessFeedback(Zone * zone,KeyedAccessMode const & keyed_mode,FeedbackSlotKind slot_kind)403 ElementAccessFeedback::ElementAccessFeedback(Zone* zone,
404                                              KeyedAccessMode const& keyed_mode,
405                                              FeedbackSlotKind slot_kind)
406     : ProcessedFeedback(kElementAccess, slot_kind),
407       keyed_mode_(keyed_mode),
408       transition_groups_(zone) {
409   DCHECK(IsKeyedLoadICKind(slot_kind) || IsKeyedHasICKind(slot_kind) ||
410          IsDefineKeyedOwnPropertyInLiteralKind(slot_kind) ||
411          IsKeyedStoreICKind(slot_kind) ||
412          IsStoreInArrayLiteralICKind(slot_kind) ||
413          IsDefineKeyedOwnICKind(slot_kind));
414 }
415 
HasOnlyStringMaps(JSHeapBroker * broker) const416 bool ElementAccessFeedback::HasOnlyStringMaps(JSHeapBroker* broker) const {
417   for (auto const& group : transition_groups()) {
418     for (Handle<Map> map : group) {
419       // We assume a memory fence because {map} was read earlier from
420       // the feedback vector and was store ordered on insertion into the
421       // vector.
422       if (!MakeRefAssumeMemoryFence(broker, map).IsStringMap()) return false;
423     }
424   }
425   return true;
426 }
427 
NamedAccessFeedback(NameRef const & name,ZoneVector<MapRef> const & maps,FeedbackSlotKind slot_kind)428 NamedAccessFeedback::NamedAccessFeedback(NameRef const& name,
429                                          ZoneVector<MapRef> const& maps,
430                                          FeedbackSlotKind slot_kind)
431     : ProcessedFeedback(kNamedAccess, slot_kind), name_(name), maps_(maps) {
432   DCHECK(IsLoadICKind(slot_kind) || IsSetNamedICKind(slot_kind) ||
433          IsDefineNamedOwnICKind(slot_kind) || IsKeyedLoadICKind(slot_kind) ||
434          IsKeyedHasICKind(slot_kind) || IsKeyedStoreICKind(slot_kind) ||
435          IsStoreInArrayLiteralICKind(slot_kind) ||
436          IsDefineKeyedOwnPropertyInLiteralKind(slot_kind) ||
437          IsDefineKeyedOwnICKind(slot_kind));
438 }
439 
SetFeedback(FeedbackSource const & source,ProcessedFeedback const * feedback)440 void JSHeapBroker::SetFeedback(FeedbackSource const& source,
441                                ProcessedFeedback const* feedback) {
442   CHECK(source.IsValid());
443   auto insertion = feedback_.insert({source, feedback});
444   CHECK(insertion.second);
445 }
446 
HasFeedback(FeedbackSource const & source) const447 bool JSHeapBroker::HasFeedback(FeedbackSource const& source) const {
448   DCHECK(source.IsValid());
449   return feedback_.find(source) != feedback_.end();
450 }
451 
GetFeedback(FeedbackSource const & source) const452 ProcessedFeedback const& JSHeapBroker::GetFeedback(
453     FeedbackSource const& source) const {
454   DCHECK(source.IsValid());
455   auto it = feedback_.find(source);
456   CHECK_NE(it, feedback_.end());
457   return *it->second;
458 }
459 
GetFeedbackSlotKind(FeedbackSource const & source) const460 FeedbackSlotKind JSHeapBroker::GetFeedbackSlotKind(
461     FeedbackSource const& source) const {
462   if (HasFeedback(source)) return GetFeedback(source).slot_kind();
463   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
464   return nexus.kind();
465 }
466 
FeedbackIsInsufficient(FeedbackSource const & source) const467 bool JSHeapBroker::FeedbackIsInsufficient(FeedbackSource const& source) const {
468   if (HasFeedback(source)) return GetFeedback(source).IsInsufficient();
469   return FeedbackNexus(source.vector, source.slot, feedback_nexus_config())
470       .IsUninitialized();
471 }
472 
NewInsufficientFeedback(FeedbackSlotKind kind) const473 const ProcessedFeedback& JSHeapBroker::NewInsufficientFeedback(
474     FeedbackSlotKind kind) const {
475   return *zone()->New<InsufficientFeedback>(kind);
476 }
477 
ReadFeedbackForPropertyAccess(FeedbackSource const & source,AccessMode mode,base::Optional<NameRef> static_name)478 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForPropertyAccess(
479     FeedbackSource const& source, AccessMode mode,
480     base::Optional<NameRef> static_name) {
481   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
482   FeedbackSlotKind kind = nexus.kind();
483   if (nexus.IsUninitialized()) return NewInsufficientFeedback(kind);
484 
485   ZoneVector<MapRef> maps(zone());
486   {
487     std::vector<MapAndHandler> maps_and_handlers_unfiltered;
488     nexus.ExtractMapsAndFeedback(&maps_and_handlers_unfiltered);
489 
490     for (const MapAndHandler& map_and_handler : maps_and_handlers_unfiltered) {
491       MapRef map = MakeRefAssumeMemoryFence(this, *map_and_handler.first);
492       // May change concurrently at any time - must be guarded by a dependency
493       // if non-deprecation is important.
494       if (map.is_deprecated()) {
495         // TODO(ishell): support fast map updating if we enable it.
496         CHECK(!FLAG_fast_map_update);
497         base::Optional<Map> maybe_map = MapUpdater::TryUpdateNoLock(
498             isolate(), *map.object(), ConcurrencyMode::kConcurrent);
499         if (maybe_map.has_value()) {
500           map = MakeRefAssumeMemoryFence(this, maybe_map.value());
501         } else {
502           continue;  // Couldn't update the deprecated map.
503         }
504       }
505       if (map.is_abandoned_prototype_map()) continue;
506       maps.push_back(map);
507     }
508   }
509 
510   base::Optional<NameRef> name =
511       static_name.has_value() ? static_name : GetNameFeedback(nexus);
512 
513   // If no maps were found for a non-megamorphic access, then our maps died
514   // and we should soft-deopt.
515   if (maps.empty() && nexus.ic_state() != InlineCacheState::MEGAMORPHIC) {
516     return NewInsufficientFeedback(kind);
517   }
518 
519   if (name.has_value()) {
520     // We rely on this invariant in JSGenericLowering.
521     DCHECK_IMPLIES(maps.empty(),
522                    nexus.ic_state() == InlineCacheState::MEGAMORPHIC);
523     return *zone()->New<NamedAccessFeedback>(*name, maps, kind);
524   } else if (nexus.GetKeyType() == IcCheckType::kElement && !maps.empty()) {
525     return ProcessFeedbackMapsForElementAccess(
526         maps, KeyedAccessMode::FromNexus(nexus), kind);
527   } else {
528     // No actionable feedback.
529     DCHECK(maps.empty());
530     DCHECK_EQ(nexus.ic_state(), InlineCacheState::MEGAMORPHIC);
531     // TODO(neis): Using ElementAccessFeedback here is kind of an abuse.
532     return *zone()->New<ElementAccessFeedback>(
533         zone(), KeyedAccessMode::FromNexus(nexus), kind);
534   }
535 }
536 
ReadFeedbackForGlobalAccess(FeedbackSource const & source)537 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForGlobalAccess(
538     FeedbackSource const& source) {
539   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
540   DCHECK(nexus.kind() == FeedbackSlotKind::kLoadGlobalInsideTypeof ||
541          nexus.kind() == FeedbackSlotKind::kLoadGlobalNotInsideTypeof ||
542          nexus.kind() == FeedbackSlotKind::kStoreGlobalSloppy ||
543          nexus.kind() == FeedbackSlotKind::kStoreGlobalStrict);
544   if (nexus.IsUninitialized()) return NewInsufficientFeedback(nexus.kind());
545   if (nexus.ic_state() != InlineCacheState::MONOMORPHIC ||
546       nexus.GetFeedback()->IsCleared()) {
547     return *zone()->New<GlobalAccessFeedback>(nexus.kind());
548   }
549 
550   Handle<Object> feedback_value =
551       CanonicalPersistentHandle(nexus.GetFeedback()->GetHeapObjectOrSmi());
552 
553   if (feedback_value->IsSmi()) {
554     // The wanted name belongs to a script-scope variable and the feedback
555     // tells us where to find its value.
556     int const number = feedback_value->Number();
557     int const script_context_index =
558         FeedbackNexus::ContextIndexBits::decode(number);
559     int const context_slot_index = FeedbackNexus::SlotIndexBits::decode(number);
560     ContextRef context = MakeRefAssumeMemoryFence(
561         this,
562         target_native_context().script_context_table().object()->get_context(
563             script_context_index, kAcquireLoad));
564 
565     base::Optional<ObjectRef> contents = context.get(context_slot_index);
566     if (contents.has_value()) CHECK(!contents->IsTheHole());
567 
568     return *zone()->New<GlobalAccessFeedback>(
569         context, context_slot_index,
570         FeedbackNexus::ImmutabilityBit::decode(number), nexus.kind());
571   }
572 
573   CHECK(feedback_value->IsPropertyCell());
574   // The wanted name belongs (or did belong) to a property on the global
575   // object and the feedback is the cell holding its value.
576   return *zone()->New<GlobalAccessFeedback>(
577       MakeRefAssumeMemoryFence(this,
578                                Handle<PropertyCell>::cast(feedback_value)),
579       nexus.kind());
580 }
581 
ReadFeedbackForBinaryOperation(FeedbackSource const & source) const582 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForBinaryOperation(
583     FeedbackSource const& source) const {
584   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
585   if (nexus.IsUninitialized()) return NewInsufficientFeedback(nexus.kind());
586   BinaryOperationHint hint = nexus.GetBinaryOperationFeedback();
587   DCHECK_NE(hint, BinaryOperationHint::kNone);  // Not uninitialized.
588   return *zone()->New<BinaryOperationFeedback>(hint, nexus.kind());
589 }
590 
ReadFeedbackForCompareOperation(FeedbackSource const & source) const591 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForCompareOperation(
592     FeedbackSource const& source) const {
593   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
594   if (nexus.IsUninitialized()) return NewInsufficientFeedback(nexus.kind());
595   CompareOperationHint hint = nexus.GetCompareOperationFeedback();
596   DCHECK_NE(hint, CompareOperationHint::kNone);  // Not uninitialized.
597   return *zone()->New<CompareOperationFeedback>(hint, nexus.kind());
598 }
599 
ReadFeedbackForForIn(FeedbackSource const & source) const600 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForForIn(
601     FeedbackSource const& source) const {
602   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
603   if (nexus.IsUninitialized()) return NewInsufficientFeedback(nexus.kind());
604   ForInHint hint = nexus.GetForInFeedback();
605   DCHECK_NE(hint, ForInHint::kNone);  // Not uninitialized.
606   return *zone()->New<ForInFeedback>(hint, nexus.kind());
607 }
608 
ReadFeedbackForInstanceOf(FeedbackSource const & source)609 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForInstanceOf(
610     FeedbackSource const& source) {
611   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
612   if (nexus.IsUninitialized()) return NewInsufficientFeedback(nexus.kind());
613 
614   base::Optional<JSObjectRef> optional_constructor;
615   {
616     MaybeHandle<JSObject> maybe_constructor = nexus.GetConstructorFeedback();
617     Handle<JSObject> constructor;
618     if (maybe_constructor.ToHandle(&constructor)) {
619       optional_constructor = MakeRefAssumeMemoryFence(this, *constructor);
620     }
621   }
622   return *zone()->New<InstanceOfFeedback>(optional_constructor, nexus.kind());
623 }
624 
ReadFeedbackForArrayOrObjectLiteral(FeedbackSource const & source)625 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForArrayOrObjectLiteral(
626     FeedbackSource const& source) {
627   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
628   if (nexus.IsUninitialized()) return NewInsufficientFeedback(nexus.kind());
629 
630   HeapObject object;
631   if (!nexus.GetFeedback()->GetHeapObject(&object)) {
632     return NewInsufficientFeedback(nexus.kind());
633   }
634 
635   AllocationSiteRef site =
636       MakeRefAssumeMemoryFence(this, AllocationSite::cast(object));
637   return *zone()->New<LiteralFeedback>(site, nexus.kind());
638 }
639 
ReadFeedbackForRegExpLiteral(FeedbackSource const & source)640 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForRegExpLiteral(
641     FeedbackSource const& source) {
642   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
643   if (nexus.IsUninitialized()) return NewInsufficientFeedback(nexus.kind());
644 
645   HeapObject object;
646   if (!nexus.GetFeedback()->GetHeapObject(&object)) {
647     return NewInsufficientFeedback(nexus.kind());
648   }
649 
650   RegExpBoilerplateDescriptionRef boilerplate = MakeRefAssumeMemoryFence(
651       this, RegExpBoilerplateDescription::cast(object));
652   return *zone()->New<RegExpLiteralFeedback>(boilerplate, nexus.kind());
653 }
654 
ReadFeedbackForTemplateObject(FeedbackSource const & source)655 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForTemplateObject(
656     FeedbackSource const& source) {
657   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
658   if (nexus.IsUninitialized()) return NewInsufficientFeedback(nexus.kind());
659 
660   HeapObject object;
661   if (!nexus.GetFeedback()->GetHeapObject(&object)) {
662     return NewInsufficientFeedback(nexus.kind());
663   }
664 
665   JSArrayRef array = MakeRefAssumeMemoryFence(this, JSArray::cast(object));
666   return *zone()->New<TemplateObjectFeedback>(array, nexus.kind());
667 }
668 
ReadFeedbackForCall(FeedbackSource const & source)669 ProcessedFeedback const& JSHeapBroker::ReadFeedbackForCall(
670     FeedbackSource const& source) {
671   FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
672   if (nexus.IsUninitialized()) return NewInsufficientFeedback(nexus.kind());
673 
674   base::Optional<HeapObjectRef> target_ref;
675   {
676     MaybeObject maybe_target = nexus.GetFeedback();
677     HeapObject target_object;
678     if (maybe_target->GetHeapObject(&target_object)) {
679       target_ref = MakeRefAssumeMemoryFence(this, target_object);
680     }
681   }
682 
683   float frequency = nexus.ComputeCallFrequency();
684   SpeculationMode mode = nexus.GetSpeculationMode();
685   CallFeedbackContent content = nexus.GetCallFeedbackContent();
686   return *zone()->New<CallFeedback>(target_ref, frequency, mode, content,
687                                     nexus.kind());
688 }
689 
GetFeedbackForBinaryOperation(FeedbackSource const & source)690 BinaryOperationHint JSHeapBroker::GetFeedbackForBinaryOperation(
691     FeedbackSource const& source) {
692   ProcessedFeedback const& feedback = ProcessFeedbackForBinaryOperation(source);
693   return feedback.IsInsufficient() ? BinaryOperationHint::kNone
694                                    : feedback.AsBinaryOperation().value();
695 }
696 
GetFeedbackForCompareOperation(FeedbackSource const & source)697 CompareOperationHint JSHeapBroker::GetFeedbackForCompareOperation(
698     FeedbackSource const& source) {
699   ProcessedFeedback const& feedback =
700       ProcessFeedbackForCompareOperation(source);
701   return feedback.IsInsufficient() ? CompareOperationHint::kNone
702                                    : feedback.AsCompareOperation().value();
703 }
704 
GetFeedbackForForIn(FeedbackSource const & source)705 ForInHint JSHeapBroker::GetFeedbackForForIn(FeedbackSource const& source) {
706   ProcessedFeedback const& feedback = ProcessFeedbackForForIn(source);
707   return feedback.IsInsufficient() ? ForInHint::kNone
708                                    : feedback.AsForIn().value();
709 }
710 
GetFeedbackForArrayOrObjectLiteral(FeedbackSource const & source)711 ProcessedFeedback const& JSHeapBroker::GetFeedbackForArrayOrObjectLiteral(
712     FeedbackSource const& source) {
713   if (HasFeedback(source)) return GetFeedback(source);
714   ProcessedFeedback const& feedback =
715       ReadFeedbackForArrayOrObjectLiteral(source);
716   SetFeedback(source, &feedback);
717   return feedback;
718 }
719 
GetFeedbackForRegExpLiteral(FeedbackSource const & source)720 ProcessedFeedback const& JSHeapBroker::GetFeedbackForRegExpLiteral(
721     FeedbackSource const& source) {
722   if (HasFeedback(source)) return GetFeedback(source);
723   ProcessedFeedback const& feedback = ReadFeedbackForRegExpLiteral(source);
724   SetFeedback(source, &feedback);
725   return feedback;
726 }
727 
GetFeedbackForTemplateObject(FeedbackSource const & source)728 ProcessedFeedback const& JSHeapBroker::GetFeedbackForTemplateObject(
729     FeedbackSource const& source) {
730   if (HasFeedback(source)) return GetFeedback(source);
731   ProcessedFeedback const& feedback = ReadFeedbackForTemplateObject(source);
732   SetFeedback(source, &feedback);
733   return feedback;
734 }
735 
ProcessFeedbackForBinaryOperation(FeedbackSource const & source)736 ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForBinaryOperation(
737     FeedbackSource const& source) {
738   if (HasFeedback(source)) return GetFeedback(source);
739   ProcessedFeedback const& feedback = ReadFeedbackForBinaryOperation(source);
740   SetFeedback(source, &feedback);
741   return feedback;
742 }
743 
ProcessFeedbackForCompareOperation(FeedbackSource const & source)744 ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForCompareOperation(
745     FeedbackSource const& source) {
746   if (HasFeedback(source)) return GetFeedback(source);
747   ProcessedFeedback const& feedback = ReadFeedbackForCompareOperation(source);
748   SetFeedback(source, &feedback);
749   return feedback;
750 }
751 
ProcessFeedbackForForIn(FeedbackSource const & source)752 ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForForIn(
753     FeedbackSource const& source) {
754   if (HasFeedback(source)) return GetFeedback(source);
755   ProcessedFeedback const& feedback = ReadFeedbackForForIn(source);
756   SetFeedback(source, &feedback);
757   return feedback;
758 }
759 
GetFeedbackForPropertyAccess(FeedbackSource const & source,AccessMode mode,base::Optional<NameRef> static_name)760 ProcessedFeedback const& JSHeapBroker::GetFeedbackForPropertyAccess(
761     FeedbackSource const& source, AccessMode mode,
762     base::Optional<NameRef> static_name) {
763   if (HasFeedback(source)) return GetFeedback(source);
764   ProcessedFeedback const& feedback =
765       ReadFeedbackForPropertyAccess(source, mode, static_name);
766   SetFeedback(source, &feedback);
767   return feedback;
768 }
769 
GetFeedbackForInstanceOf(FeedbackSource const & source)770 ProcessedFeedback const& JSHeapBroker::GetFeedbackForInstanceOf(
771     FeedbackSource const& source) {
772   if (HasFeedback(source)) return GetFeedback(source);
773   ProcessedFeedback const& feedback = ReadFeedbackForInstanceOf(source);
774   SetFeedback(source, &feedback);
775   return feedback;
776 }
777 
GetFeedbackForCall(FeedbackSource const & source)778 ProcessedFeedback const& JSHeapBroker::GetFeedbackForCall(
779     FeedbackSource const& source) {
780   if (HasFeedback(source)) return GetFeedback(source);
781   ProcessedFeedback const& feedback = ReadFeedbackForCall(source);
782   SetFeedback(source, &feedback);
783   return feedback;
784 }
785 
GetFeedbackForGlobalAccess(FeedbackSource const & source)786 ProcessedFeedback const& JSHeapBroker::GetFeedbackForGlobalAccess(
787     FeedbackSource const& source) {
788   if (HasFeedback(source)) return GetFeedback(source);
789   ProcessedFeedback const& feedback = ReadFeedbackForGlobalAccess(source);
790   SetFeedback(source, &feedback);
791   return feedback;
792 }
793 
ProcessFeedbackMapsForElementAccess(ZoneVector<MapRef> & maps,KeyedAccessMode const & keyed_mode,FeedbackSlotKind slot_kind)794 ElementAccessFeedback const& JSHeapBroker::ProcessFeedbackMapsForElementAccess(
795     ZoneVector<MapRef>& maps, KeyedAccessMode const& keyed_mode,
796     FeedbackSlotKind slot_kind) {
797   DCHECK(!maps.empty());
798 
799   // Collect possible transition targets.
800   MapHandles possible_transition_targets;
801   possible_transition_targets.reserve(maps.size());
802   for (MapRef& map : maps) {
803     if (map.CanInlineElementAccess() &&
804         IsFastElementsKind(map.elements_kind()) &&
805         GetInitialFastElementsKind() != map.elements_kind()) {
806       possible_transition_targets.push_back(map.object());
807     }
808   }
809 
810   using TransitionGroup = ElementAccessFeedback::TransitionGroup;
811   struct HandleLess {
812     bool operator()(Handle<Map> x, Handle<Map> y) const {
813       return x.address() < y.address();
814     }
815   };
816   ZoneMap<Handle<Map>, TransitionGroup, HandleLess> transition_groups(zone());
817 
818   // Separate the actual receiver maps and the possible transition sources.
819   for (const MapRef& map : maps) {
820     Map transition_target;
821 
822     // Don't generate elements kind transitions from stable maps.
823     if (!map.is_stable()) {
824       // The lock is needed for UnusedPropertyFields (called deep inside
825       // FindElementsKindTransitionedMap).
826       MapUpdaterGuardIfNeeded mumd_scope(this);
827 
828       transition_target = map.object()->FindElementsKindTransitionedMap(
829           isolate(), possible_transition_targets, ConcurrencyMode::kConcurrent);
830     }
831 
832     if (transition_target.is_null()) {
833       TransitionGroup group(1, map.object(), zone());
834       transition_groups.insert({map.object(), group});
835     } else {
836       Handle<Map> target = CanonicalPersistentHandle(transition_target);
837       TransitionGroup new_group(1, target, zone());
838       TransitionGroup& actual_group =
839           transition_groups.insert({target, new_group}).first->second;
840       actual_group.push_back(map.object());
841     }
842   }
843 
844   ElementAccessFeedback* result =
845       zone()->New<ElementAccessFeedback>(zone(), keyed_mode, slot_kind);
846   for (auto entry : transition_groups) {
847     result->AddGroup(std::move(entry.second));
848   }
849 
850   CHECK(!result->transition_groups().empty());
851   return *result;
852 }
853 
AddGroup(TransitionGroup && group)854 void ElementAccessFeedback::AddGroup(TransitionGroup&& group) {
855   CHECK(!group.empty());
856   transition_groups_.push_back(std::move(group));
857 
858 #ifdef ENABLE_SLOW_DCHECKS
859   // Check that each of the group's maps occurs exactly once in the whole
860   // feedback. This implies that "a source is not a target".
861   for (Handle<Map> map : group) {
862     int count = 0;
863     for (TransitionGroup const& some_group : transition_groups()) {
864       count += std::count_if(
865           some_group.begin(), some_group.end(),
866           [&](Handle<Map> some_map) { return some_map.equals(map); });
867     }
868     CHECK_EQ(count, 1);
869   }
870 #endif
871 }
872 
GetNameFeedback(FeedbackNexus const & nexus)873 base::Optional<NameRef> JSHeapBroker::GetNameFeedback(
874     FeedbackNexus const& nexus) {
875   Name raw_name = nexus.GetName();
876   if (raw_name.is_null()) return base::nullopt;
877   return MakeRefAssumeMemoryFence(this, raw_name);
878 }
879 
GetPropertyAccessInfo(MapRef map,NameRef name,AccessMode access_mode,CompilationDependencies * dependencies)880 PropertyAccessInfo JSHeapBroker::GetPropertyAccessInfo(
881     MapRef map, NameRef name, AccessMode access_mode,
882     CompilationDependencies* dependencies) {
883   DCHECK_NOT_NULL(dependencies);
884 
885   PropertyAccessTarget target({map, name, access_mode});
886   auto it = property_access_infos_.find(target);
887   if (it != property_access_infos_.end()) return it->second;
888 
889   AccessInfoFactory factory(this, dependencies, zone());
890   PropertyAccessInfo access_info =
891       factory.ComputePropertyAccessInfo(map, name, access_mode);
892   TRACE(this, "Storing PropertyAccessInfo for "
893                   << access_mode << " of property " << name << " on map "
894                   << map);
895   property_access_infos_.insert({target, access_info});
896   return access_info;
897 }
898 
AsBinaryOperation() const899 BinaryOperationFeedback const& ProcessedFeedback::AsBinaryOperation() const {
900   CHECK_EQ(kBinaryOperation, kind());
901   return *static_cast<BinaryOperationFeedback const*>(this);
902 }
903 
AsCall() const904 CallFeedback const& ProcessedFeedback::AsCall() const {
905   CHECK_EQ(kCall, kind());
906   return *static_cast<CallFeedback const*>(this);
907 }
908 
AsCompareOperation() const909 CompareOperationFeedback const& ProcessedFeedback::AsCompareOperation() const {
910   CHECK_EQ(kCompareOperation, kind());
911   return *static_cast<CompareOperationFeedback const*>(this);
912 }
913 
AsElementAccess() const914 ElementAccessFeedback const& ProcessedFeedback::AsElementAccess() const {
915   CHECK_EQ(kElementAccess, kind());
916   return *static_cast<ElementAccessFeedback const*>(this);
917 }
918 
AsForIn() const919 ForInFeedback const& ProcessedFeedback::AsForIn() const {
920   CHECK_EQ(kForIn, kind());
921   return *static_cast<ForInFeedback const*>(this);
922 }
923 
AsGlobalAccess() const924 GlobalAccessFeedback const& ProcessedFeedback::AsGlobalAccess() const {
925   CHECK_EQ(kGlobalAccess, kind());
926   return *static_cast<GlobalAccessFeedback const*>(this);
927 }
928 
AsInstanceOf() const929 InstanceOfFeedback const& ProcessedFeedback::AsInstanceOf() const {
930   CHECK_EQ(kInstanceOf, kind());
931   return *static_cast<InstanceOfFeedback const*>(this);
932 }
933 
AsNamedAccess() const934 NamedAccessFeedback const& ProcessedFeedback::AsNamedAccess() const {
935   CHECK_EQ(kNamedAccess, kind());
936   return *static_cast<NamedAccessFeedback const*>(this);
937 }
938 
AsLiteral() const939 LiteralFeedback const& ProcessedFeedback::AsLiteral() const {
940   CHECK_EQ(kLiteral, kind());
941   return *static_cast<LiteralFeedback const*>(this);
942 }
943 
AsRegExpLiteral() const944 RegExpLiteralFeedback const& ProcessedFeedback::AsRegExpLiteral() const {
945   CHECK_EQ(kRegExpLiteral, kind());
946   return *static_cast<RegExpLiteralFeedback const*>(this);
947 }
948 
AsTemplateObject() const949 TemplateObjectFeedback const& ProcessedFeedback::AsTemplateObject() const {
950   CHECK_EQ(kTemplateObject, kind());
951   return *static_cast<TemplateObjectFeedback const*>(this);
952 }
953 
954 #undef TRACE
955 
956 }  // namespace compiler
957 }  // namespace internal
958 }  // namespace v8
959