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