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