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
27namespace v8 {
28namespace internal {
29namespace 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.
37const uint32_t JSHeapBroker::kMinimalRefsBucketCount;
38const uint32_t JSHeapBroker::kInitialRefsBucketCount;
39#endif
40
41void JSHeapBroker::IncrementTracingIndentation() { ++trace_indentation_; }
42
43void JSHeapBroker::DecrementTracingIndentation() { --trace_indentation_; }
44
45JSHeapBroker::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
68JSHeapBroker::~JSHeapBroker() { DCHECK_NULL(local_isolate_); }
69
70void 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
77void 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
91std::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
98void 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
108void 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
118void JSHeapBroker::StopSerializing() {
119  CHECK_EQ(mode_, kSerializing);
120  TRACE(this, "Stopping serialization");
121  mode_ = kSerialized;
122}
123
124void JSHeapBroker::Retire() {
125  CHECK_EQ(mode_, kSerialized);
126  TRACE(this, "Retiring");
127  mode_ = kRetired;
128}
129
130void 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
138void 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
157StringRef 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
170bool JSHeapBroker::IsArrayOrObjectPrototype(const JSObjectRef& object) const {
171  return IsArrayOrObjectPrototype(object.object());
172}
173
174bool 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
186ObjectData* JSHeapBroker::TryGetOrCreateData(Object object,
187                                             GetOrCreateDataFlags flags) {
188  return TryGetOrCreateData(CanonicalPersistentHandle(object), flags);
189}
190
191ObjectData* 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
198ObjectData* JSHeapBroker::GetOrCreateData(Object object,
199                                          GetOrCreateDataFlags flags) {
200  return GetOrCreateData(CanonicalPersistentHandle(object), flags);
201}
202
203bool 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
211bool JSHeapBroker::ObjectMayBeUninitialized(Handle<Object> object) const {
212  return ObjectMayBeUninitialized(*object);
213}
214
215bool JSHeapBroker::ObjectMayBeUninitialized(Object object) const {
216  if (!object.IsHeapObject()) return false;
217  return ObjectMayBeUninitialized(HeapObject::cast(object));
218}
219
220bool JSHeapBroker::ObjectMayBeUninitialized(HeapObject object) const {
221  return !IsMainThread() && isolate()->heap()->IsPendingAllocation(object);
222}
223
224ProcessedFeedback::ProcessedFeedback(Kind kind, FeedbackSlotKind slot_kind)
225    : kind_(kind), slot_kind_(slot_kind) {}
226
227KeyedAccessMode ElementAccessFeedback::keyed_mode() const {
228  return keyed_mode_;
229}
230
231ZoneVector<ElementAccessFeedback::TransitionGroup> const&
232ElementAccessFeedback::transition_groups() const {
233  return transition_groups_;
234}
235
236ElementAccessFeedback 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
274InsufficientFeedback::InsufficientFeedback(FeedbackSlotKind slot_kind)
275    : ProcessedFeedback(kInsufficient, slot_kind) {}
276
277GlobalAccessFeedback::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
285GlobalAccessFeedback::GlobalAccessFeedback(FeedbackSlotKind slot_kind)
286    : ProcessedFeedback(kGlobalAccess, slot_kind),
287      index_and_immutable_(0 /* doesn't matter */) {
288  DCHECK(IsGlobalICKind(slot_kind));
289}
290
291GlobalAccessFeedback::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
303bool GlobalAccessFeedback::IsMegamorphic() const {
304  return !cell_or_context_.has_value();
305}
306bool GlobalAccessFeedback::IsPropertyCell() const {
307  return cell_or_context_.has_value() && cell_or_context_->IsPropertyCell();
308}
309bool GlobalAccessFeedback::IsScriptContextSlot() const {
310  return cell_or_context_.has_value() && cell_or_context_->IsContext();
311}
312PropertyCellRef GlobalAccessFeedback::property_cell() const {
313  CHECK(IsPropertyCell());
314  return cell_or_context_->AsPropertyCell();
315}
316ContextRef GlobalAccessFeedback::script_context() const {
317  CHECK(IsScriptContextSlot());
318  return cell_or_context_->AsContext();
319}
320int GlobalAccessFeedback::slot_index() const {
321  DCHECK(IsScriptContextSlot());
322  return FeedbackNexus::SlotIndexBits::decode(index_and_immutable_);
323}
324bool GlobalAccessFeedback::immutable() const {
325  DCHECK(IsScriptContextSlot());
326  return FeedbackNexus::ImmutabilityBit::decode(index_and_immutable_);
327}
328
329base::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
341KeyedAccessMode 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
364AccessMode KeyedAccessMode::access_mode() const { return access_mode_; }
365
366bool KeyedAccessMode::IsLoad() const {
367  return access_mode_ == AccessMode::kLoad || access_mode_ == AccessMode::kHas;
368}
369bool KeyedAccessMode::IsStore() const {
370  return access_mode_ == AccessMode::kStore ||
371         access_mode_ == AccessMode::kDefine ||
372         access_mode_ == AccessMode::kStoreInLiteral;
373}
374
375KeyedAccessLoadMode KeyedAccessMode::load_mode() const {
376  CHECK(IsLoad());
377  return load_store_mode_.load_mode;
378}
379
380KeyedAccessStoreMode KeyedAccessMode::store_mode() const {
381  CHECK(IsStore());
382  return load_store_mode_.store_mode;
383}
384
385KeyedAccessMode::LoadStoreMode::LoadStoreMode(KeyedAccessLoadMode load_mode)
386    : load_mode(load_mode) {}
387KeyedAccessMode::LoadStoreMode::LoadStoreMode(KeyedAccessStoreMode store_mode)
388    : store_mode(store_mode) {}
389
390KeyedAccessMode::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}
396KeyedAccessMode::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
403ElementAccessFeedback::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
416bool 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
428NamedAccessFeedback::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
440void 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
447bool JSHeapBroker::HasFeedback(FeedbackSource const& source) const {
448  DCHECK(source.IsValid());
449  return feedback_.find(source) != feedback_.end();
450}
451
452ProcessedFeedback 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
460FeedbackSlotKind 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
467bool 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
473const ProcessedFeedback& JSHeapBroker::NewInsufficientFeedback(
474    FeedbackSlotKind kind) const {
475  return *zone()->New<InsufficientFeedback>(kind);
476}
477
478ProcessedFeedback 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
537ProcessedFeedback 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
582ProcessedFeedback 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
591ProcessedFeedback 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
600ProcessedFeedback 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
609ProcessedFeedback 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
625ProcessedFeedback 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
640ProcessedFeedback 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
655ProcessedFeedback 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
669ProcessedFeedback 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
690BinaryOperationHint JSHeapBroker::GetFeedbackForBinaryOperation(
691    FeedbackSource const& source) {
692  ProcessedFeedback const& feedback = ProcessFeedbackForBinaryOperation(source);
693  return feedback.IsInsufficient() ? BinaryOperationHint::kNone
694                                   : feedback.AsBinaryOperation().value();
695}
696
697CompareOperationHint 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
705ForInHint JSHeapBroker::GetFeedbackForForIn(FeedbackSource const& source) {
706  ProcessedFeedback const& feedback = ProcessFeedbackForForIn(source);
707  return feedback.IsInsufficient() ? ForInHint::kNone
708                                   : feedback.AsForIn().value();
709}
710
711ProcessedFeedback 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
720ProcessedFeedback 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
728ProcessedFeedback 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
736ProcessedFeedback 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
744ProcessedFeedback 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
752ProcessedFeedback 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
760ProcessedFeedback 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
770ProcessedFeedback 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
778ProcessedFeedback 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
786ProcessedFeedback 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
794ElementAccessFeedback 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
854void 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
873base::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
880PropertyAccessInfo 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
899BinaryOperationFeedback const& ProcessedFeedback::AsBinaryOperation() const {
900  CHECK_EQ(kBinaryOperation, kind());
901  return *static_cast<BinaryOperationFeedback const*>(this);
902}
903
904CallFeedback const& ProcessedFeedback::AsCall() const {
905  CHECK_EQ(kCall, kind());
906  return *static_cast<CallFeedback const*>(this);
907}
908
909CompareOperationFeedback const& ProcessedFeedback::AsCompareOperation() const {
910  CHECK_EQ(kCompareOperation, kind());
911  return *static_cast<CompareOperationFeedback const*>(this);
912}
913
914ElementAccessFeedback const& ProcessedFeedback::AsElementAccess() const {
915  CHECK_EQ(kElementAccess, kind());
916  return *static_cast<ElementAccessFeedback const*>(this);
917}
918
919ForInFeedback const& ProcessedFeedback::AsForIn() const {
920  CHECK_EQ(kForIn, kind());
921  return *static_cast<ForInFeedback const*>(this);
922}
923
924GlobalAccessFeedback const& ProcessedFeedback::AsGlobalAccess() const {
925  CHECK_EQ(kGlobalAccess, kind());
926  return *static_cast<GlobalAccessFeedback const*>(this);
927}
928
929InstanceOfFeedback const& ProcessedFeedback::AsInstanceOf() const {
930  CHECK_EQ(kInstanceOf, kind());
931  return *static_cast<InstanceOfFeedback const*>(this);
932}
933
934NamedAccessFeedback const& ProcessedFeedback::AsNamedAccess() const {
935  CHECK_EQ(kNamedAccess, kind());
936  return *static_cast<NamedAccessFeedback const*>(this);
937}
938
939LiteralFeedback const& ProcessedFeedback::AsLiteral() const {
940  CHECK_EQ(kLiteral, kind());
941  return *static_cast<LiteralFeedback const*>(this);
942}
943
944RegExpLiteralFeedback const& ProcessedFeedback::AsRegExpLiteral() const {
945  CHECK_EQ(kRegExpLiteral, kind());
946  return *static_cast<RegExpLiteralFeedback const*>(this);
947}
948
949TemplateObjectFeedback 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