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 #ifndef V8_COMPILER_JS_HEAP_BROKER_H_ 6 #define V8_COMPILER_JS_HEAP_BROKER_H_ 7 8 #include "src/base/compiler-specific.h" 9 #include "src/base/optional.h" 10 #include "src/common/globals.h" 11 #include "src/compiler/access-info.h" 12 #include "src/compiler/feedback-source.h" 13 #include "src/compiler/globals.h" 14 #include "src/compiler/processed-feedback.h" 15 #include "src/compiler/refs-map.h" 16 #include "src/compiler/serializer-hints.h" 17 #include "src/execution/local-isolate.h" 18 #include "src/handles/handles.h" 19 #include "src/handles/persistent-handles.h" 20 #include "src/heap/local-heap.h" 21 #include "src/interpreter/bytecode-array-accessor.h" 22 #include "src/objects/code-kind.h" 23 #include "src/objects/feedback-vector.h" 24 #include "src/objects/function-kind.h" 25 #include "src/objects/objects.h" 26 #include "src/utils/address-map.h" 27 #include "src/utils/identity-map.h" 28 #include "src/utils/ostreams.h" 29 #include "src/zone/zone-containers.h" 30 31 namespace v8 { 32 namespace internal { 33 namespace compiler { 34 35 class BytecodeAnalysis; 36 class ObjectRef; 37 38 std::ostream& operator<<(std::ostream& os, const ObjectRef& ref); 39 40 #define TRACE_BROKER(broker, x) \ 41 do { \ 42 if (broker->tracing_enabled() && FLAG_trace_heap_broker_verbose) \ 43 StdoutStream{} << broker->Trace() << x << '\n'; \ 44 } while (false) 45 46 #define TRACE_BROKER_MEMORY(broker, x) \ 47 do { \ 48 if (broker->tracing_enabled() && FLAG_trace_heap_broker_memory) \ 49 StdoutStream{} << broker->Trace() << x << std::endl; \ 50 } while (false) 51 52 #define TRACE_BROKER_MISSING(broker, x) \ 53 do { \ 54 if (broker->tracing_enabled()) \ 55 StdoutStream{} << broker->Trace() << "Missing " << x << " (" << __FILE__ \ 56 << ":" << __LINE__ << ")" << std::endl; \ 57 } while (false) 58 59 struct PropertyAccessTarget { 60 MapRef map; 61 NameRef name; 62 AccessMode mode; 63 64 struct Hash { operatorPropertyAccessTarget::Hash65 size_t operator()(const PropertyAccessTarget& pair) const { 66 return base::hash_combine( 67 base::hash_combine(pair.map.object().address(), 68 pair.name.object().address()), 69 static_cast<int>(pair.mode)); 70 } 71 }; 72 struct Equal { operatorPropertyAccessTarget::Equal73 bool operator()(const PropertyAccessTarget& lhs, 74 const PropertyAccessTarget& rhs) const { 75 return lhs.map.equals(rhs.map) && lhs.name.equals(rhs.name) && 76 lhs.mode == rhs.mode; 77 } 78 }; 79 }; 80 81 class V8_EXPORT_PRIVATE JSHeapBroker { 82 public: 83 JSHeapBroker(Isolate* isolate, Zone* broker_zone, bool tracing_enabled, 84 bool is_concurrent_inlining, CodeKind code_kind); 85 86 // For use only in tests, sets default values for some arguments. Avoids 87 // churn when new flags are added. JSHeapBroker(Isolate * isolate,Zone * broker_zone)88 JSHeapBroker(Isolate* isolate, Zone* broker_zone) 89 : JSHeapBroker(isolate, broker_zone, FLAG_trace_heap_broker, false, 90 CodeKind::TURBOFAN) {} 91 92 ~JSHeapBroker(); 93 94 // The compilation target's native context. We need the setter because at 95 // broker construction time we don't yet have the canonical handle. target_native_context()96 NativeContextRef target_native_context() const { 97 return target_native_context_.value(); 98 } 99 void SetTargetNativeContextRef(Handle<NativeContext> native_context); 100 101 void InitializeAndStartSerializing(Handle<NativeContext> native_context); 102 isolate()103 Isolate* isolate() const { return isolate_; } zone()104 Zone* zone() const { return zone_; } tracing_enabled()105 bool tracing_enabled() const { return tracing_enabled_; } is_concurrent_inlining()106 bool is_concurrent_inlining() const { return is_concurrent_inlining_; } is_native_context_independent()107 bool is_native_context_independent() const { 108 return code_kind_ == CodeKind::NATIVE_CONTEXT_INDEPENDENT; 109 } generate_full_feedback_collection()110 bool generate_full_feedback_collection() const { 111 // NCI code currently collects full feedback. 112 DCHECK_IMPLIES(is_native_context_independent(), 113 CollectFeedbackInGenericLowering()); 114 return is_native_context_independent(); 115 } is_turboprop()116 bool is_turboprop() const { return code_kind_ == CodeKind::TURBOPROP; } 117 feedback_nexus_config()118 NexusConfig feedback_nexus_config() const { 119 // TODO(mvstanton): when the broker gathers feedback on the background 120 // thread, this should return a local NexusConfig object which points 121 // to the associated LocalHeap. 122 return NexusConfig::FromMainThread(isolate()); 123 } 124 125 enum BrokerMode { kDisabled, kSerializing, kSerialized, kRetired }; mode()126 BrokerMode mode() const { return mode_; } 127 128 void StopSerializing(); 129 void Retire(); 130 bool SerializingAllowed() const; 131 132 // Remember the local isolate and initialize its local heap with the 133 // persistent and canonical handles provided by {info}. 134 void AttachLocalIsolate(OptimizedCompilationInfo* info, 135 LocalIsolate* local_isolate); 136 // Forget about the local isolate and pass the persistent and canonical 137 // handles provided back to {info}. {info} is responsible for disposing of 138 // them. 139 void DetachLocalIsolate(OptimizedCompilationInfo* info); 140 141 bool StackHasOverflowed() const; 142 143 #ifdef DEBUG 144 void PrintRefsAnalysis() const; 145 #endif // DEBUG 146 147 // Retruns the handle from root index table for read only heap objects. 148 Handle<Object> GetRootHandle(Object object); 149 150 // Never returns nullptr. 151 ObjectData* GetOrCreateData(Handle<Object>); 152 // Like the previous but wraps argument in handle first (for convenience). 153 ObjectData* GetOrCreateData(Object); 154 155 // Check if {object} is any native context's %ArrayPrototype% or 156 // %ObjectPrototype%. 157 bool IsArrayOrObjectPrototype(const JSObjectRef& object) const; 158 159 bool HasFeedback(FeedbackSource const& source) const; 160 void SetFeedback(FeedbackSource const& source, 161 ProcessedFeedback const* feedback); 162 ProcessedFeedback const& GetFeedback(FeedbackSource const& source) const; 163 FeedbackSlotKind GetFeedbackSlotKind(FeedbackSource const& source) const; 164 165 // TODO(neis): Move these into serializer when we're always in the background. 166 ElementAccessFeedback const& ProcessFeedbackMapsForElementAccess( 167 MapHandles const& maps, KeyedAccessMode const& keyed_mode, 168 FeedbackSlotKind slot_kind); 169 BytecodeAnalysis const& GetBytecodeAnalysis( 170 Handle<BytecodeArray> bytecode_array, BailoutId osr_offset, 171 bool analyze_liveness, 172 SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); 173 174 // Binary, comparison and for-in hints can be fully expressed via 175 // an enum. Insufficient feedback is signaled by <Hint enum>::kNone. 176 BinaryOperationHint GetFeedbackForBinaryOperation( 177 FeedbackSource const& source); 178 CompareOperationHint GetFeedbackForCompareOperation( 179 FeedbackSource const& source); 180 ForInHint GetFeedbackForForIn(FeedbackSource const& source); 181 182 ProcessedFeedback const& GetFeedbackForCall(FeedbackSource const& source); 183 ProcessedFeedback const& GetFeedbackForGlobalAccess( 184 FeedbackSource const& source); 185 ProcessedFeedback const& GetFeedbackForInstanceOf( 186 FeedbackSource const& source); 187 ProcessedFeedback const& GetFeedbackForArrayOrObjectLiteral( 188 FeedbackSource const& source); 189 ProcessedFeedback const& GetFeedbackForRegExpLiteral( 190 FeedbackSource const& source); 191 ProcessedFeedback const& GetFeedbackForTemplateObject( 192 FeedbackSource const& source); 193 ProcessedFeedback const& GetFeedbackForPropertyAccess( 194 FeedbackSource const& source, AccessMode mode, 195 base::Optional<NameRef> static_name); 196 197 ProcessedFeedback const& ProcessFeedbackForBinaryOperation( 198 FeedbackSource const& source); 199 ProcessedFeedback const& ProcessFeedbackForCall(FeedbackSource const& source); 200 ProcessedFeedback const& ProcessFeedbackForCompareOperation( 201 FeedbackSource const& source); 202 ProcessedFeedback const& ProcessFeedbackForForIn( 203 FeedbackSource const& source); 204 ProcessedFeedback const& ProcessFeedbackForGlobalAccess( 205 FeedbackSource const& source); 206 ProcessedFeedback const& ProcessFeedbackForInstanceOf( 207 FeedbackSource const& source); 208 ProcessedFeedback const& ProcessFeedbackForPropertyAccess( 209 FeedbackSource const& source, AccessMode mode, 210 base::Optional<NameRef> static_name); 211 ProcessedFeedback const& ProcessFeedbackForArrayOrObjectLiteral( 212 FeedbackSource const& source); 213 ProcessedFeedback const& ProcessFeedbackForRegExpLiteral( 214 FeedbackSource const& source); 215 ProcessedFeedback const& ProcessFeedbackForTemplateObject( 216 FeedbackSource const& source); 217 218 bool FeedbackIsInsufficient(FeedbackSource const& source) const; 219 220 base::Optional<NameRef> GetNameFeedback(FeedbackNexus const& nexus); 221 222 // If {policy} is {kAssumeSerialized} and the broker doesn't know about the 223 // combination of {map}, {name}, and {access_mode}, returns Invalid. 224 PropertyAccessInfo GetPropertyAccessInfo( 225 MapRef map, NameRef name, AccessMode access_mode, 226 CompilationDependencies* dependencies = nullptr, 227 SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); 228 229 MinimorphicLoadPropertyAccessInfo GetPropertyAccessInfo( 230 MinimorphicLoadPropertyAccessFeedback const& feedback, 231 FeedbackSource const& source, 232 SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); 233 234 StringRef GetTypedArrayStringTag(ElementsKind kind); 235 236 bool ShouldBeSerializedForCompilation(const SharedFunctionInfoRef& shared, 237 const FeedbackVectorRef& feedback, 238 const HintsVector& arguments) const; 239 void SetSerializedForCompilation(const SharedFunctionInfoRef& shared, 240 const FeedbackVectorRef& feedback, 241 const HintsVector& arguments); 242 bool IsSerializedForCompilation(const SharedFunctionInfoRef& shared, 243 const FeedbackVectorRef& feedback) const; 244 local_isolate()245 LocalIsolate* local_isolate() const { return local_isolate_; } 246 247 // Return the corresponding canonical persistent handle for {object}. Create 248 // one if it does not exist. 249 // If we have the canonical map, we can create the canonical & persistent 250 // handle through it. This commonly happens during the Execute phase. 251 // If we don't, that means we are calling this method from serialization. If 252 // that happens, we should be inside a canonical and a persistent handle 253 // scope. Then, we would just use the regular handle creation. 254 template <typename T> CanonicalPersistentHandle(T object)255 Handle<T> CanonicalPersistentHandle(T object) { 256 if (canonical_handles_) { 257 Address address = object.ptr(); 258 if (Internals::HasHeapObjectTag(address)) { 259 RootIndex root_index; 260 if (root_index_map_.Lookup(address, &root_index)) { 261 return Handle<T>(isolate_->root_handle(root_index).location()); 262 } 263 } 264 265 Object obj(address); 266 auto find_result = canonical_handles_->FindOrInsert(obj); 267 if (!find_result.already_exists) { 268 // Allocate new PersistentHandle if one wasn't created before. 269 DCHECK_NOT_NULL(local_isolate()); 270 *find_result.entry = 271 local_isolate()->heap()->NewPersistentHandle(obj).location(); 272 } 273 return Handle<T>(*find_result.entry); 274 } else { 275 return Handle<T>(object, isolate()); 276 } 277 } 278 279 template <typename T> CanonicalPersistentHandle(Handle<T> object)280 Handle<T> CanonicalPersistentHandle(Handle<T> object) { 281 return CanonicalPersistentHandle<T>(*object); 282 } 283 284 // Find the corresponding handle in the CanonicalHandlesMap. The entry must be 285 // found. 286 template <typename T> FindCanonicalPersistentHandleForTesting(Object object)287 Handle<T> FindCanonicalPersistentHandleForTesting(Object object) { 288 Address** entry = canonical_handles_->Find(object); 289 return Handle<T>(*entry); 290 } 291 292 // Set the persistent handles and copy the canonical handles over to the 293 // JSHeapBroker. 294 void SetPersistentAndCopyCanonicalHandlesForTesting( 295 std::unique_ptr<PersistentHandles> persistent_handles, 296 std::unique_ptr<CanonicalHandlesMap> canonical_handles); 297 std::string Trace() const; 298 void IncrementTracingIndentation(); 299 void DecrementTracingIndentation(); 300 root_index_map()301 RootIndexMap const& root_index_map() { return root_index_map_; } 302 303 private: 304 friend class HeapObjectRef; 305 friend class ObjectRef; 306 friend class ObjectData; 307 308 bool CanUseFeedback(const FeedbackNexus& nexus) const; 309 const ProcessedFeedback& NewInsufficientFeedback(FeedbackSlotKind kind) const; 310 311 // Bottleneck FeedbackNexus access here, for storage in the broker 312 // or on-the-fly usage elsewhere in the compiler. 313 ProcessedFeedback const& ReadFeedbackForArrayOrObjectLiteral( 314 FeedbackSource const& source); 315 ProcessedFeedback const& ReadFeedbackForBinaryOperation( 316 FeedbackSource const& source) const; 317 ProcessedFeedback const& ReadFeedbackForCall(FeedbackSource const& source); 318 ProcessedFeedback const& ReadFeedbackForCompareOperation( 319 FeedbackSource const& source) const; 320 ProcessedFeedback const& ReadFeedbackForForIn( 321 FeedbackSource const& source) const; 322 ProcessedFeedback const& ReadFeedbackForGlobalAccess( 323 FeedbackSource const& source); 324 ProcessedFeedback const& ReadFeedbackForInstanceOf( 325 FeedbackSource const& source); 326 ProcessedFeedback const& ReadFeedbackForPropertyAccess( 327 FeedbackSource const& source, AccessMode mode, 328 base::Optional<NameRef> static_name); 329 ProcessedFeedback const& ReadFeedbackForRegExpLiteral( 330 FeedbackSource const& source); 331 ProcessedFeedback const& ReadFeedbackForTemplateObject( 332 FeedbackSource const& source); 333 334 void CollectArrayAndObjectPrototypes(); 335 compiler_cache()336 PerIsolateCompilerCache* compiler_cache() const { return compiler_cache_; } 337 set_persistent_handles(std::unique_ptr<PersistentHandles> persistent_handles)338 void set_persistent_handles( 339 std::unique_ptr<PersistentHandles> persistent_handles) { 340 DCHECK_NULL(ph_); 341 ph_ = std::move(persistent_handles); 342 DCHECK_NOT_NULL(ph_); 343 } DetachPersistentHandles()344 std::unique_ptr<PersistentHandles> DetachPersistentHandles() { 345 DCHECK_NOT_NULL(ph_); 346 return std::move(ph_); 347 } 348 set_canonical_handles(std::unique_ptr<CanonicalHandlesMap> canonical_handles)349 void set_canonical_handles( 350 std::unique_ptr<CanonicalHandlesMap> canonical_handles) { 351 DCHECK_NULL(canonical_handles_); 352 canonical_handles_ = std::move(canonical_handles); 353 DCHECK_NOT_NULL(canonical_handles_); 354 } 355 DetachCanonicalHandles()356 std::unique_ptr<CanonicalHandlesMap> DetachCanonicalHandles() { 357 DCHECK_NOT_NULL(canonical_handles_); 358 return std::move(canonical_handles_); 359 } 360 361 // Copy the canonical handles over to the JSHeapBroker. 362 void CopyCanonicalHandlesForTesting( 363 std::unique_ptr<CanonicalHandlesMap> canonical_handles); 364 365 Isolate* const isolate_; 366 Zone* const zone_ = nullptr; 367 base::Optional<NativeContextRef> target_native_context_; 368 RefsMap* refs_; 369 RootIndexMap root_index_map_; 370 ZoneUnorderedSet<Handle<JSObject>, Handle<JSObject>::hash, 371 Handle<JSObject>::equal_to> 372 array_and_object_prototypes_; 373 BrokerMode mode_ = kDisabled; 374 bool const tracing_enabled_; 375 bool const is_concurrent_inlining_; 376 CodeKind const code_kind_; 377 std::unique_ptr<PersistentHandles> ph_; 378 LocalIsolate* local_isolate_ = nullptr; 379 std::unique_ptr<CanonicalHandlesMap> canonical_handles_; 380 unsigned trace_indentation_ = 0; 381 PerIsolateCompilerCache* compiler_cache_ = nullptr; 382 ZoneUnorderedMap<FeedbackSource, ProcessedFeedback const*, 383 FeedbackSource::Hash, FeedbackSource::Equal> 384 feedback_; 385 ZoneUnorderedMap<ObjectData*, BytecodeAnalysis*> bytecode_analyses_; 386 ZoneUnorderedMap<PropertyAccessTarget, PropertyAccessInfo, 387 PropertyAccessTarget::Hash, PropertyAccessTarget::Equal> 388 property_access_infos_; 389 ZoneUnorderedMap<FeedbackSource, MinimorphicLoadPropertyAccessInfo, 390 FeedbackSource::Hash, FeedbackSource::Equal> 391 minimorphic_property_access_infos_; 392 393 ZoneVector<ObjectData*> typed_array_string_tags_; 394 395 struct SerializedFunction { 396 SharedFunctionInfoRef shared; 397 FeedbackVectorRef feedback; 398 399 bool operator<(const SerializedFunction& other) const { 400 if (shared.object().address() < other.shared.object().address()) { 401 return true; 402 } 403 if (shared.object().address() == other.shared.object().address()) { 404 return feedback.object().address() < other.feedback.object().address(); 405 } 406 return false; 407 } 408 }; 409 ZoneMultimap<SerializedFunction, HintsVector> serialized_functions_; 410 411 static const size_t kMaxSerializedFunctionsCacheSize = 200; 412 static const uint32_t kMinimalRefsBucketCount = 8; // must be power of 2 413 static const uint32_t kInitialRefsBucketCount = 1024; // must be power of 2 414 }; 415 416 class TraceScope { 417 public: TraceScope(JSHeapBroker * broker,const char * label)418 TraceScope(JSHeapBroker* broker, const char* label) 419 : TraceScope(broker, static_cast<void*>(broker), label) {} 420 TraceScope(JSHeapBroker * broker,ObjectData * data,const char * label)421 TraceScope(JSHeapBroker* broker, ObjectData* data, const char* label) 422 : TraceScope(broker, static_cast<void*>(data), label) {} 423 TraceScope(JSHeapBroker * broker,void * subject,const char * label)424 TraceScope(JSHeapBroker* broker, void* subject, const char* label) 425 : broker_(broker) { 426 TRACE_BROKER(broker_, "Running " << label << " on " << subject); 427 broker_->IncrementTracingIndentation(); 428 } 429 ~TraceScope()430 ~TraceScope() { broker_->DecrementTracingIndentation(); } 431 432 private: 433 JSHeapBroker* const broker_; 434 }; 435 436 #define ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(something_var, \ 437 optionally_something) \ 438 auto optionally_something_ = optionally_something; \ 439 if (!optionally_something_) \ 440 return NoChangeBecauseOfMissingData(broker(), __FUNCTION__, __LINE__); \ 441 something_var = *optionally_something_; 442 443 class Reduction; 444 Reduction NoChangeBecauseOfMissingData(JSHeapBroker* broker, 445 const char* function, int line); 446 447 // Miscellaneous definitions that should be moved elsewhere once concurrent 448 // compilation is finished. 449 bool CanInlineElementAccess(MapRef const& map); 450 451 class OffHeapBytecodeArray final : public interpreter::AbstractBytecodeArray { 452 public: 453 explicit OffHeapBytecodeArray(BytecodeArrayRef bytecode_array); 454 455 int length() const override; 456 int parameter_count() const override; 457 uint8_t get(int index) const override; 458 void set(int index, uint8_t value) override; 459 Address GetFirstBytecodeAddress() const override; 460 Handle<Object> GetConstantAtIndex(int index, Isolate* isolate) const override; 461 bool IsConstantAtIndexSmi(int index) const override; 462 Smi GetConstantAtIndexAsSmi(int index) const override; 463 464 private: 465 BytecodeArrayRef array_; 466 }; 467 468 // Scope that unparks the LocalHeap, if: 469 // a) We have a JSHeapBroker, 470 // b) Said JSHeapBroker has a LocalIsolate and thus a LocalHeap, 471 // c) Said LocalHeap has been parked and 472 // d) The given condition evaluates to true. 473 // Used, for example, when printing the graph with --trace-turbo with a 474 // previously parked LocalHeap. 475 class UnparkedScopeIfNeeded { 476 public: 477 explicit UnparkedScopeIfNeeded(JSHeapBroker* broker, 478 bool extra_condition = true) { 479 if (broker != nullptr && extra_condition) { 480 LocalIsolate* local_isolate = broker->local_isolate(); 481 if (local_isolate != nullptr && local_isolate->heap()->IsParked()) { 482 unparked_scope.emplace(local_isolate->heap()); 483 } 484 } 485 } 486 487 private: 488 base::Optional<UnparkedScope> unparked_scope; 489 }; 490 491 } // namespace compiler 492 } // namespace internal 493 } // namespace v8 494 495 #endif // V8_COMPILER_JS_HEAP_BROKER_H_ 496