• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #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