js-heap-broker.h 19.7 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_COMPILER_JS_HEAP_BROKER_H_
#define V8_COMPILER_JS_HEAP_BROKER_H_

#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 20
#include "src/handles/persistent-handles.h"
#include "src/heap/local-heap.h"
21
#include "src/heap/parked-scope.h"
22
#include "src/interpreter/bytecode-array-accessor.h"
23
#include "src/objects/code-kind.h"
24 25 26
#include "src/objects/feedback-vector.h"
#include "src/objects/function-kind.h"
#include "src/objects/objects.h"
27
#include "src/utils/address-map.h"
28
#include "src/utils/identity-map.h"
29
#include "src/utils/ostreams.h"
30
#include "src/zone/zone-containers.h"
31 32 33 34 35

namespace v8 {
namespace internal {
namespace compiler {

36
class ObjectRef;
37

38 39
std::ostream& operator<<(std::ostream& os, const ObjectRef& ref);

40 41 42
#define TRACE_BROKER(broker, x)                                      \
  do {                                                               \
    if (broker->tracing_enabled() && FLAG_trace_heap_broker_verbose) \
43
      StdoutStream{} << broker->Trace() << x << '\n';                \
44 45
  } while (false)

46 47 48
#define TRACE_BROKER_MEMORY(broker, x)                              \
  do {                                                              \
    if (broker->tracing_enabled() && FLAG_trace_heap_broker_memory) \
49
      StdoutStream{} << broker->Trace() << x << std::endl;          \
50 51
  } while (false)

52 53 54 55 56
#define TRACE_BROKER_MISSING(broker, x)                                        \
  do {                                                                         \
    if (broker->tracing_enabled())                                             \
      StdoutStream{} << broker->Trace() << "Missing " << x << " (" << __FILE__ \
                     << ":" << __LINE__ << ")" << std::endl;                   \
57 58
  } while (false)

59
struct PropertyAccessTarget {
60 61
  MapRef map;
  NameRef name;
62
  AccessMode mode;
63 64

  struct Hash {
65 66 67 68 69
    size_t operator()(const PropertyAccessTarget& pair) const {
      return base::hash_combine(
          base::hash_combine(pair.map.object().address(),
                             pair.name.object().address()),
          static_cast<int>(pair.mode));
70 71 72
    }
  };
  struct Equal {
73 74 75 76
    bool operator()(const PropertyAccessTarget& lhs,
                    const PropertyAccessTarget& rhs) const {
      return lhs.map.equals(rhs.map) && lhs.name.equals(rhs.name) &&
             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 87

  // For use only in tests, sets default values for some arguments. Avoids
  // churn when new flags are added.
88 89
  JSHeapBroker(Isolate* isolate, Zone* broker_zone)
      : JSHeapBroker(isolate, broker_zone, FLAG_trace_heap_broker, false,
90
                     CodeKind::TURBOFAN) {}
91 92

  ~JSHeapBroker();
93

94 95 96 97 98 99 100 101
  // The compilation target's native context. We need the setter because at
  // broker construction time we don't yet have the canonical handle.
  NativeContextRef target_native_context() const {
    return target_native_context_.value();
  }
  void SetTargetNativeContextRef(Handle<NativeContext> native_context);

  void InitializeAndStartSerializing(Handle<NativeContext> native_context);
102 103

  Isolate* isolate() const { return isolate_; }
104
  Zone* zone() const { return zone_; }
105
  bool tracing_enabled() const { return tracing_enabled_; }
106
  bool is_concurrent_inlining() const { return is_concurrent_inlining_; }
107
  bool is_isolate_bootstrapping() const { return is_isolate_bootstrapping_; }
108
  bool is_native_context_independent() const {
109
    return code_kind_ == CodeKind::NATIVE_CONTEXT_INDEPENDENT;
110
  }
111 112 113 114 115 116
  bool generate_full_feedback_collection() const {
    // NCI code currently collects full feedback.
    DCHECK_IMPLIES(is_native_context_independent(),
                   CollectFeedbackInGenericLowering());
    return is_native_context_independent();
  }
117
  bool is_turboprop() const { return code_kind_ == CodeKind::TURBOPROP; }
118

119 120 121 122 123 124 125
  NexusConfig feedback_nexus_config() const {
    // TODO(mvstanton): when the broker gathers feedback on the background
    // thread, this should return a local NexusConfig object which points
    // to the associated LocalHeap.
    return NexusConfig::FromMainThread(isolate());
  }

126
  enum BrokerMode { kDisabled, kSerializing, kSerialized, kRetired };
127
  BrokerMode mode() const { return mode_; }
128

129 130
  void StopSerializing();
  void Retire();
131
  bool SerializingAllowed() const;
132

133 134 135 136 137 138 139 140 141 142 143
  // Remember the local isolate and initialize its local heap with the
  // persistent and canonical handles provided by {info}.
  void AttachLocalIsolate(OptimizedCompilationInfo* info,
                          LocalIsolate* local_isolate);
  // Forget about the local isolate and pass the persistent and canonical
  // handles provided back to {info}. {info} is responsible for disposing of
  // them.
  void DetachLocalIsolate(OptimizedCompilationInfo* info);

  bool StackHasOverflowed() const;

144 145 146 147
#ifdef DEBUG
  void PrintRefsAnalysis() const;
#endif  // DEBUG

148 149 150
  // Retruns the handle from root index table for read only heap objects.
  Handle<Object> GetRootHandle(Object object);

151
  // Never returns nullptr.
152 153 154 155
  ObjectData* GetOrCreateData(
      Handle<Object>,
      ObjectRef::BackgroundSerialization background_serialization =
          ObjectRef::BackgroundSerialization::kDisallowed);
156
  // Like the previous but wraps argument in handle first (for convenience).
157 158 159
  ObjectData* GetOrCreateData(
      Object, ObjectRef::BackgroundSerialization background_serialization =
                  ObjectRef::BackgroundSerialization::kDisallowed);
160

161 162 163 164 165 166 167
  // Gets data only if we have it. However, thin wrappers will be created for
  // smis, read-only objects and never-serialized objects.
  ObjectData* TryGetOrCreateData(
      Handle<Object>, bool crash_on_error,
      ObjectRef::BackgroundSerialization background_serialization =
          ObjectRef::BackgroundSerialization::kDisallowed);

168 169 170 171
  // Check if {object} is any native context's %ArrayPrototype% or
  // %ObjectPrototype%.
  bool IsArrayOrObjectPrototype(const JSObjectRef& object) const;

172 173 174
  bool HasFeedback(FeedbackSource const& source) const;
  void SetFeedback(FeedbackSource const& source,
                   ProcessedFeedback const* feedback);
175
  ProcessedFeedback const& GetFeedback(FeedbackSource const& source) const;
176
  FeedbackSlotKind GetFeedbackSlotKind(FeedbackSource const& source) const;
177

178
  // TODO(neis): Move these into serializer when we're always in the background.
179
  ElementAccessFeedback const& ProcessFeedbackMapsForElementAccess(
180 181
      MapHandles const& maps, KeyedAccessMode const& keyed_mode,
      FeedbackSlotKind slot_kind);
182 183 184 185

  // Binary, comparison and for-in hints can be fully expressed via
  // an enum. Insufficient feedback is signaled by <Hint enum>::kNone.
  BinaryOperationHint GetFeedbackForBinaryOperation(
186
      FeedbackSource const& source);
187
  CompareOperationHint GetFeedbackForCompareOperation(
188 189
      FeedbackSource const& source);
  ForInHint GetFeedbackForForIn(FeedbackSource const& source);
190 191 192 193 194 195

  ProcessedFeedback const& GetFeedbackForCall(FeedbackSource const& source);
  ProcessedFeedback const& GetFeedbackForGlobalAccess(
      FeedbackSource const& source);
  ProcessedFeedback const& GetFeedbackForInstanceOf(
      FeedbackSource const& source);
196 197 198 199 200 201
  ProcessedFeedback const& GetFeedbackForArrayOrObjectLiteral(
      FeedbackSource const& source);
  ProcessedFeedback const& GetFeedbackForRegExpLiteral(
      FeedbackSource const& source);
  ProcessedFeedback const& GetFeedbackForTemplateObject(
      FeedbackSource const& source);
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
  ProcessedFeedback const& GetFeedbackForPropertyAccess(
      FeedbackSource const& source, AccessMode mode,
      base::Optional<NameRef> static_name);

  ProcessedFeedback const& ProcessFeedbackForBinaryOperation(
      FeedbackSource const& source);
  ProcessedFeedback const& ProcessFeedbackForCall(FeedbackSource const& source);
  ProcessedFeedback const& ProcessFeedbackForCompareOperation(
      FeedbackSource const& source);
  ProcessedFeedback const& ProcessFeedbackForForIn(
      FeedbackSource const& source);
  ProcessedFeedback const& ProcessFeedbackForGlobalAccess(
      FeedbackSource const& source);
  ProcessedFeedback const& ProcessFeedbackForInstanceOf(
      FeedbackSource const& source);
  ProcessedFeedback const& ProcessFeedbackForPropertyAccess(
      FeedbackSource const& source, AccessMode mode,
      base::Optional<NameRef> static_name);
220 221 222 223 224 225
  ProcessedFeedback const& ProcessFeedbackForArrayOrObjectLiteral(
      FeedbackSource const& source);
  ProcessedFeedback const& ProcessFeedbackForRegExpLiteral(
      FeedbackSource const& source);
  ProcessedFeedback const& ProcessFeedbackForTemplateObject(
      FeedbackSource const& source);
226 227

  bool FeedbackIsInsufficient(FeedbackSource const& source) const;
228

229 230
  base::Optional<NameRef> GetNameFeedback(FeedbackNexus const& nexus);

231 232
  // If {policy} is {kAssumeSerialized} and the broker doesn't know about the
  // combination of {map}, {name}, and {access_mode}, returns Invalid.
233 234
  PropertyAccessInfo GetPropertyAccessInfo(
      MapRef map, NameRef name, AccessMode access_mode,
235
      CompilationDependencies* dependencies = nullptr,
236
      SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
237

238 239 240 241 242
  MinimorphicLoadPropertyAccessInfo GetPropertyAccessInfo(
      MinimorphicLoadPropertyAccessFeedback const& feedback,
      FeedbackSource const& source,
      SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);

243 244
  StringRef GetTypedArrayStringTag(ElementsKind kind);

245 246 247 248 249 250 251 252 253
  bool ShouldBeSerializedForCompilation(const SharedFunctionInfoRef& shared,
                                        const FeedbackVectorRef& feedback,
                                        const HintsVector& arguments) const;
  void SetSerializedForCompilation(const SharedFunctionInfoRef& shared,
                                   const FeedbackVectorRef& feedback,
                                   const HintsVector& arguments);
  bool IsSerializedForCompilation(const SharedFunctionInfoRef& shared,
                                  const FeedbackVectorRef& feedback) const;

254
  LocalIsolate* local_isolate() const { return local_isolate_; }
255 256 257 258 259 260 261 262

  // Return the corresponding canonical persistent handle for {object}. Create
  // one if it does not exist.
  // If we have the canonical map, we can create the canonical & persistent
  // handle through it. This commonly happens during the Execute phase.
  // If we don't, that means we are calling this method from serialization. If
  // that happens, we should be inside a canonical and a persistent handle
  // scope. Then, we would just use the regular handle creation.
263
  template <typename T>
264 265 266 267 268 269 270 271 272 273 274
  Handle<T> CanonicalPersistentHandle(T object) {
    if (canonical_handles_) {
      Address address = object.ptr();
      if (Internals::HasHeapObjectTag(address)) {
        RootIndex root_index;
        if (root_index_map_.Lookup(address, &root_index)) {
          return Handle<T>(isolate_->root_handle(root_index).location());
        }
      }

      Object obj(address);
275 276
      auto find_result = canonical_handles_->FindOrInsert(obj);
      if (!find_result.already_exists) {
277
        // Allocate new PersistentHandle if one wasn't created before.
278 279 280
        DCHECK_NOT_NULL(local_isolate());
        *find_result.entry =
            local_isolate()->heap()->NewPersistentHandle(obj).location();
281
      }
282
      return Handle<T>(*find_result.entry);
283 284 285
    } else {
      return Handle<T>(object, isolate());
    }
286 287 288
  }

  template <typename T>
289 290
  Handle<T> CanonicalPersistentHandle(Handle<T> object) {
    return CanonicalPersistentHandle<T>(*object);
291 292
  }

293 294 295 296 297 298
  // Find the corresponding handle in the CanonicalHandlesMap. The entry must be
  // found.
  template <typename T>
  Handle<T> FindCanonicalPersistentHandleForTesting(Object object) {
    Address** entry = canonical_handles_->Find(object);
    return Handle<T>(*entry);
299 300
  }

301 302 303 304 305
  // Set the persistent handles and copy the canonical handles over to the
  // JSHeapBroker.
  void SetPersistentAndCopyCanonicalHandlesForTesting(
      std::unique_ptr<PersistentHandles> persistent_handles,
      std::unique_ptr<CanonicalHandlesMap> canonical_handles);
306
  std::string Trace() const;
307 308
  void IncrementTracingIndentation();
  void DecrementTracingIndentation();
309

310 311
  RootIndexMap const& root_index_map() { return root_index_map_; }

312 313
 private:
  friend class HeapObjectRef;
314
  friend class ObjectRef;
315
  friend class ObjectData;
316

317 318 319
  bool CanUseFeedback(const FeedbackNexus& nexus) const;
  const ProcessedFeedback& NewInsufficientFeedback(FeedbackSlotKind kind) const;

320 321
  // Bottleneck FeedbackNexus access here, for storage in the broker
  // or on-the-fly usage elsewhere in the compiler.
322 323 324
  ProcessedFeedback const& ReadFeedbackForArrayOrObjectLiteral(
      FeedbackSource const& source);
  ProcessedFeedback const& ReadFeedbackForBinaryOperation(
325 326
      FeedbackSource const& source) const;
  ProcessedFeedback const& ReadFeedbackForCall(FeedbackSource const& source);
327 328 329 330
  ProcessedFeedback const& ReadFeedbackForCompareOperation(
      FeedbackSource const& source) const;
  ProcessedFeedback const& ReadFeedbackForForIn(
      FeedbackSource const& source) const;
331 332 333 334 335 336 337
  ProcessedFeedback const& ReadFeedbackForGlobalAccess(
      FeedbackSource const& source);
  ProcessedFeedback const& ReadFeedbackForInstanceOf(
      FeedbackSource const& source);
  ProcessedFeedback const& ReadFeedbackForPropertyAccess(
      FeedbackSource const& source, AccessMode mode,
      base::Optional<NameRef> static_name);
338 339 340 341
  ProcessedFeedback const& ReadFeedbackForRegExpLiteral(
      FeedbackSource const& source);
  ProcessedFeedback const& ReadFeedbackForTemplateObject(
      FeedbackSource const& source);
342

343
  void CollectArrayAndObjectPrototypes();
344

345 346
  PerIsolateCompilerCache* compiler_cache() const { return compiler_cache_; }

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
  void set_persistent_handles(
      std::unique_ptr<PersistentHandles> persistent_handles) {
    DCHECK_NULL(ph_);
    ph_ = std::move(persistent_handles);
    DCHECK_NOT_NULL(ph_);
  }
  std::unique_ptr<PersistentHandles> DetachPersistentHandles() {
    DCHECK_NOT_NULL(ph_);
    return std::move(ph_);
  }

  void set_canonical_handles(
      std::unique_ptr<CanonicalHandlesMap> canonical_handles) {
    DCHECK_NULL(canonical_handles_);
    canonical_handles_ = std::move(canonical_handles);
    DCHECK_NOT_NULL(canonical_handles_);
  }

  std::unique_ptr<CanonicalHandlesMap> DetachCanonicalHandles() {
    DCHECK_NOT_NULL(canonical_handles_);
    return std::move(canonical_handles_);
  }

  // Copy the canonical handles over to the JSHeapBroker.
  void CopyCanonicalHandlesForTesting(
      std::unique_ptr<CanonicalHandlesMap> canonical_handles);

374
  Isolate* const isolate_;
375
  Zone* const zone_ = nullptr;
376
  base::Optional<NativeContextRef> target_native_context_;
377
  RefsMap* refs_;
378
  RootIndexMap root_index_map_;
379 380 381
  ZoneUnorderedSet<Handle<JSObject>, Handle<JSObject>::hash,
                   Handle<JSObject>::equal_to>
      array_and_object_prototypes_;
382
  BrokerMode mode_ = kDisabled;
383
  bool const tracing_enabled_;
384
  bool const is_concurrent_inlining_;
385
  bool const is_isolate_bootstrapping_;
386
  CodeKind const code_kind_;
387
  std::unique_ptr<PersistentHandles> ph_;
388
  LocalIsolate* local_isolate_ = nullptr;
389
  std::unique_ptr<CanonicalHandlesMap> canonical_handles_;
390
  unsigned trace_indentation_ = 0;
391
  PerIsolateCompilerCache* compiler_cache_ = nullptr;
392 393
  ZoneUnorderedMap<FeedbackSource, ProcessedFeedback const*,
                   FeedbackSource::Hash, FeedbackSource::Equal>
394
      feedback_;
395 396 397
  ZoneUnorderedMap<PropertyAccessTarget, PropertyAccessInfo,
                   PropertyAccessTarget::Hash, PropertyAccessTarget::Equal>
      property_access_infos_;
398 399
  ZoneUnorderedMap<FeedbackSource, MinimorphicLoadPropertyAccessInfo,
                   FeedbackSource::Hash, FeedbackSource::Equal>
400
      minimorphic_property_access_infos_;
401

402 403
  ZoneVector<ObjectData*> typed_array_string_tags_;

404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
  struct SerializedFunction {
    SharedFunctionInfoRef shared;
    FeedbackVectorRef feedback;

    bool operator<(const SerializedFunction& other) const {
      if (shared.object().address() < other.shared.object().address()) {
        return true;
      }
      if (shared.object().address() == other.shared.object().address()) {
        return feedback.object().address() < other.feedback.object().address();
      }
      return false;
    }
  };
  ZoneMultimap<SerializedFunction, HintsVector> serialized_functions_;

420
  static const size_t kMaxSerializedFunctionsCacheSize = 200;
421 422
  static const uint32_t kMinimalRefsBucketCount = 8;     // must be power of 2
  static const uint32_t kInitialRefsBucketCount = 1024;  // must be power of 2
423 424
};

425
class V8_NODISCARD TraceScope {
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
 public:
  TraceScope(JSHeapBroker* broker, const char* label)
      : TraceScope(broker, static_cast<void*>(broker), label) {}

  TraceScope(JSHeapBroker* broker, ObjectData* data, const char* label)
      : TraceScope(broker, static_cast<void*>(data), label) {}

  TraceScope(JSHeapBroker* broker, void* subject, const char* label)
      : broker_(broker) {
    TRACE_BROKER(broker_, "Running " << label << " on " << subject);
    broker_->IncrementTracingIndentation();
  }

  ~TraceScope() { broker_->DecrementTracingIndentation(); }

 private:
  JSHeapBroker* const broker_;
};

445 446 447 448 449
#define ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(something_var,             \
                                                optionally_something)      \
  auto optionally_something_ = optionally_something;                       \
  if (!optionally_something_)                                              \
    return NoChangeBecauseOfMissingData(broker(), __FUNCTION__, __LINE__); \
450 451 452 453 454 455
  something_var = *optionally_something_;

class Reduction;
Reduction NoChangeBecauseOfMissingData(JSHeapBroker* broker,
                                       const char* function, int line);

456 457
// Miscellaneous definitions that should be moved elsewhere once concurrent
// compilation is finished.
458
bool CanInlineElementAccess(MapRef const& map);
459

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
class OffHeapBytecodeArray final : public interpreter::AbstractBytecodeArray {
 public:
  explicit OffHeapBytecodeArray(BytecodeArrayRef bytecode_array);

  int length() const override;
  int parameter_count() const override;
  uint8_t get(int index) const override;
  void set(int index, uint8_t value) override;
  Address GetFirstBytecodeAddress() const override;
  Handle<Object> GetConstantAtIndex(int index, Isolate* isolate) const override;
  bool IsConstantAtIndexSmi(int index) const override;
  Smi GetConstantAtIndexAsSmi(int index) const override;

 private:
  BytecodeArrayRef array_;
};

477 478
// Scope that unparks the LocalHeap, if:
//   a) We have a JSHeapBroker,
479
//   b) Said JSHeapBroker has a LocalIsolate and thus a LocalHeap,
480 481
//   c) Said LocalHeap has been parked and
//   d) The given condition evaluates to true.
482 483
// Used, for example, when printing the graph with --trace-turbo with a
// previously parked LocalHeap.
484
class V8_NODISCARD UnparkedScopeIfNeeded {
485
 public:
486 487 488
  explicit UnparkedScopeIfNeeded(JSHeapBroker* broker,
                                 bool extra_condition = true) {
    if (broker != nullptr && extra_condition) {
489 490 491
      LocalIsolate* local_isolate = broker->local_isolate();
      if (local_isolate != nullptr && local_isolate->heap()->IsParked()) {
        unparked_scope.emplace(local_isolate->heap());
492 493 494 495 496 497 498 499
      }
    }
  }

 private:
  base::Optional<UnparkedScope> unparked_scope;
};

500 501 502 503 504
}  // namespace compiler
}  // namespace internal
}  // namespace v8

#endif  // V8_COMPILER_JS_HEAP_BROKER_H_