V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
compilation-dependencies.cc
1 // Copyright 2015 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/compilation-dependencies.h"
6 
7 #include "src/handles-inl.h"
8 #include "src/objects-inl.h"
9 
10 namespace v8 {
11 namespace internal {
12 namespace compiler {
13 
14 CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone)
15  : zone_(zone), dependencies_(zone) {}
16 
18  public:
19  virtual bool IsValid() const = 0;
20  virtual void PrepareInstall() {}
21  virtual void Install(const MaybeObjectHandle& code) = 0;
22 };
23 
25  public:
26  // TODO(neis): Once the concurrent compiler frontend is always-on, we no
27  // longer need to explicitly store the initial map.
28  InitialMapDependency(const JSFunctionRef& function, const MapRef& initial_map)
29  : function_(function), initial_map_(initial_map) {
30  DCHECK(function_.has_initial_map());
31  DCHECK(function_.initial_map().equals(initial_map_));
32  }
33 
34  bool IsValid() const override {
35  Handle<JSFunction> function = function_.object();
36  return function->has_initial_map() &&
37  function->initial_map() == *initial_map_.object();
38  }
39 
40  void Install(const MaybeObjectHandle& code) override {
41  SLOW_DCHECK(IsValid());
42  DependentCode::InstallDependency(function_.isolate(), code,
43  initial_map_.object(),
44  DependentCode::kInitialMapChangedGroup);
45  }
46 
47  private:
48  JSFunctionRef function_;
49  MapRef initial_map_;
50 };
51 
54  public:
55  // TODO(neis): Once the concurrent compiler frontend is always-on, we no
56  // longer need to explicitly store the prototype.
58  const ObjectRef& prototype)
59  : function_(function), prototype_(prototype) {
60  DCHECK(function_.has_prototype());
61  DCHECK(!function_.PrototypeRequiresRuntimeLookup());
62  DCHECK(function_.prototype().equals(prototype_));
63  }
64 
65  bool IsValid() const override {
66  Handle<JSFunction> function = function_.object();
67  return function->has_prototype_slot() && function->has_prototype() &&
68  !function->PrototypeRequiresRuntimeLookup() &&
69  function->prototype() == *prototype_.object();
70  }
71 
72  void PrepareInstall() override {
73  SLOW_DCHECK(IsValid());
74  Handle<JSFunction> function = function_.object();
75  if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function);
76  }
77 
78  void Install(const MaybeObjectHandle& code) override {
79  SLOW_DCHECK(IsValid());
80  Handle<JSFunction> function = function_.object();
81  DCHECK(function->has_initial_map());
82  Handle<Map> initial_map(function->initial_map(), function_.isolate());
83  DependentCode::InstallDependency(function_.isolate(), code, initial_map,
84  DependentCode::kInitialMapChangedGroup);
85  }
86 
87  private:
88  JSFunctionRef function_;
89  ObjectRef prototype_;
90 };
91 
93  public:
94  explicit StableMapDependency(const MapRef& map) : map_(map) {
95  DCHECK(map_.is_stable());
96  }
97 
98  bool IsValid() const override { return map_.object()->is_stable(); }
99 
100  void Install(const MaybeObjectHandle& code) override {
101  SLOW_DCHECK(IsValid());
102  DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
103  DependentCode::kPrototypeCheckGroup);
104  }
105 
106  private:
107  MapRef map_;
108 };
109 
111  public:
112  explicit TransitionDependency(const MapRef& map) : map_(map) {
113  DCHECK(!map_.is_deprecated());
114  }
115 
116  bool IsValid() const override { return !map_.object()->is_deprecated(); }
117 
118  void Install(const MaybeObjectHandle& code) override {
119  SLOW_DCHECK(IsValid());
120  DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
121  DependentCode::kTransitionGroup);
122  }
123 
124  private:
125  MapRef map_;
126 };
127 
130  public:
131  // TODO(neis): Once the concurrent compiler frontend is always-on, we no
132  // longer need to explicitly store the mode.
133  PretenureModeDependency(const AllocationSiteRef& site, PretenureFlag mode)
134  : site_(site), mode_(mode) {
135  DCHECK_EQ(mode_, site_.GetPretenureMode());
136  }
137 
138  bool IsValid() const override {
139  return mode_ == site_.object()->GetPretenureMode();
140  }
141 
142  void Install(const MaybeObjectHandle& code) override {
143  SLOW_DCHECK(IsValid());
144  DependentCode::InstallDependency(
145  site_.isolate(), code, site_.object(),
146  DependentCode::kAllocationSiteTenuringChangedGroup);
147  }
148 
149  private:
150  AllocationSiteRef site_;
151  PretenureFlag mode_;
152 };
153 
155  public:
156  // TODO(neis): Once the concurrent compiler frontend is always-on, we no
157  // longer need to explicitly store the type.
158  FieldTypeDependency(const MapRef& owner, int descriptor,
159  const ObjectRef& type, PropertyConstness constness)
160  : owner_(owner),
161  descriptor_(descriptor),
162  type_(type),
163  constness_(constness) {
164  DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_)));
165  DCHECK(type_.equals(owner_.GetFieldType(descriptor_)));
166  DCHECK_EQ(constness_, owner_.GetPropertyDetails(descriptor_).constness());
167  }
168 
169  bool IsValid() const override {
170  DisallowHeapAllocation no_heap_allocation;
171  Handle<Map> owner = owner_.object();
172  Handle<Object> type = type_.object();
173  return *type == owner->instance_descriptors()->GetFieldType(descriptor_) &&
174  constness_ == owner->instance_descriptors()
175  ->GetDetails(descriptor_)
176  .constness();
177  }
178 
179  void Install(const MaybeObjectHandle& code) override {
180  SLOW_DCHECK(IsValid());
181  DependentCode::InstallDependency(owner_.isolate(), code, owner_.object(),
182  DependentCode::kFieldOwnerGroup);
183  }
184 
185  private:
186  MapRef owner_;
187  int descriptor_;
188  ObjectRef type_;
189  PropertyConstness constness_;
190 };
191 
194  public:
195  // TODO(neis): Once the concurrent compiler frontend is always-on, we no
196  // longer need to explicitly store the type and the read_only flag.
197  GlobalPropertyDependency(const PropertyCellRef& cell, PropertyCellType type,
198  bool read_only)
199  : cell_(cell), type_(type), read_only_(read_only) {
200  DCHECK_EQ(type_, cell_.property_details().cell_type());
201  DCHECK_EQ(read_only_, cell_.property_details().IsReadOnly());
202  }
203 
204  bool IsValid() const override {
205  Handle<PropertyCell> cell = cell_.object();
206  // The dependency is never valid if the cell is 'invalidated'. This is
207  // marked by setting the value to the hole.
208  if (cell->value() == *(cell_.isolate()->factory()->the_hole_value())) {
209  DCHECK(cell->property_details().cell_type() ==
210  PropertyCellType::kInvalidated ||
211  cell->property_details().cell_type() ==
212  PropertyCellType::kUninitialized);
213  return false;
214  }
215  return type_ == cell->property_details().cell_type() &&
216  read_only_ == cell->property_details().IsReadOnly();
217  }
218 
219  void Install(const MaybeObjectHandle& code) override {
220  SLOW_DCHECK(IsValid());
221  DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
222  DependentCode::kPropertyCellChangedGroup);
223  }
224 
225  private:
226  PropertyCellRef cell_;
227  PropertyCellType type_;
228  bool read_only_;
229 };
230 
232  public:
233  explicit ProtectorDependency(const PropertyCellRef& cell) : cell_(cell) {
234  DCHECK_EQ(cell_.value().AsSmi(), Isolate::kProtectorValid);
235  }
236 
237  bool IsValid() const override {
238  Handle<PropertyCell> cell = cell_.object();
239  return cell->value() == Smi::FromInt(Isolate::kProtectorValid);
240  }
241 
242  void Install(const MaybeObjectHandle& code) override {
243  SLOW_DCHECK(IsValid());
244  DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
245  DependentCode::kPropertyCellChangedGroup);
246  }
247 
248  private:
249  PropertyCellRef cell_;
250 };
251 
254  public:
255  // TODO(neis): Once the concurrent compiler frontend is always-on, we no
256  // longer need to explicitly store the elements kind.
257  ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind)
258  : site_(site), kind_(kind) {
259  DCHECK(AllocationSite::ShouldTrack(kind_));
260  DCHECK_EQ(kind_, site_.PointsToLiteral()
261  ? site_.boilerplate().value().GetElementsKind()
262  : site_.GetElementsKind());
263  }
264 
265  bool IsValid() const override {
266  Handle<AllocationSite> site = site_.object();
267  ElementsKind kind = site->PointsToLiteral()
268  ? site->boilerplate()->GetElementsKind()
269  : site->GetElementsKind();
270  return kind_ == kind;
271  }
272 
273  void Install(const MaybeObjectHandle& code) override {
274  SLOW_DCHECK(IsValid());
275  DependentCode::InstallDependency(
276  site_.isolate(), code, site_.object(),
277  DependentCode::kAllocationSiteTransitionChangedGroup);
278  }
279 
280  private:
281  AllocationSiteRef site_;
282  ElementsKind kind_;
283 };
284 
287  public:
289  int instance_size)
290  : function_(function), instance_size_(instance_size) {}
291 
292  bool IsValid() const override {
293  // The dependency is valid if the prediction is the same as the current
294  // slack tracking result.
295  if (!function_.object()->has_initial_map()) return false;
296  int instance_size = function_.object()->ComputeInstanceSizeWithMinSlack(
297  function_.isolate());
298  return instance_size == instance_size_;
299  }
300 
301  void PrepareInstall() override {
302  SLOW_DCHECK(IsValid());
303  function_.object()->CompleteInobjectSlackTrackingIfActive();
304  }
305 
306  void Install(const MaybeObjectHandle& code) override {
307  SLOW_DCHECK(IsValid());
308  DCHECK(!function_.object()
309  ->initial_map()
310  ->IsInobjectSlackTrackingInProgress());
311  }
312 
313  private:
314  JSFunctionRef function_;
315  int instance_size_;
316 };
317 
318 MapRef CompilationDependencies::DependOnInitialMap(
319  const JSFunctionRef& function) {
320  MapRef map = function.initial_map();
321  dependencies_.push_front(new (zone_) InitialMapDependency(function, map));
322  return map;
323 }
324 
325 ObjectRef CompilationDependencies::DependOnPrototypeProperty(
326  const JSFunctionRef& function) {
327  ObjectRef prototype = function.prototype();
328  dependencies_.push_front(
329  new (zone_) PrototypePropertyDependency(function, prototype));
330  return prototype;
331 }
332 
333 void CompilationDependencies::DependOnStableMap(const MapRef& map) {
334  if (map.CanTransition()) {
335  dependencies_.push_front(new (zone_) StableMapDependency(map));
336  } else {
337  DCHECK(map.is_stable());
338  }
339 }
340 
341 void CompilationDependencies::DependOnTransition(const MapRef& target_map) {
342  if (target_map.CanBeDeprecated()) {
343  dependencies_.push_front(new (zone_) TransitionDependency(target_map));
344  } else {
345  DCHECK(!target_map.is_deprecated());
346  }
347 }
348 
349 PretenureFlag CompilationDependencies::DependOnPretenureMode(
350  const AllocationSiteRef& site) {
351  PretenureFlag mode = site.GetPretenureMode();
352  dependencies_.push_front(new (zone_) PretenureModeDependency(site, mode));
353  return mode;
354 }
355 
356 void CompilationDependencies::DependOnFieldType(const MapRef& map,
357  int descriptor) {
358  MapRef owner = map.FindFieldOwner(descriptor);
359  ObjectRef type = owner.GetFieldType(descriptor);
360  PropertyConstness constness =
361  owner.GetPropertyDetails(descriptor).constness();
362  DCHECK(type.equals(map.GetFieldType(descriptor)));
363  dependencies_.push_front(
364  new (zone_) FieldTypeDependency(owner, descriptor, type, constness));
365 }
366 
367 void CompilationDependencies::DependOnGlobalProperty(
368  const PropertyCellRef& cell) {
369  PropertyCellType type = cell.property_details().cell_type();
370  bool read_only = cell.property_details().IsReadOnly();
371  dependencies_.push_front(new (zone_)
372  GlobalPropertyDependency(cell, type, read_only));
373 }
374 
375 void CompilationDependencies::DependOnProtector(const PropertyCellRef& cell) {
376  dependencies_.push_front(new (zone_) ProtectorDependency(cell));
377 }
378 
379 void CompilationDependencies::DependOnElementsKind(
380  const AllocationSiteRef& site) {
381  // Do nothing if the object doesn't have any useful element transitions left.
382  ElementsKind kind = site.PointsToLiteral()
383  ? site.boilerplate().value().GetElementsKind()
384  : site.GetElementsKind();
385  if (AllocationSite::ShouldTrack(kind)) {
386  dependencies_.push_front(new (zone_) ElementsKindDependency(site, kind));
387  }
388 }
389 
390 bool CompilationDependencies::AreValid() const {
391  for (auto dep : dependencies_) {
392  if (!dep->IsValid()) return false;
393  }
394  return true;
395 }
396 
397 bool CompilationDependencies::Commit(Handle<Code> code) {
398  for (auto dep : dependencies_) {
399  if (!dep->IsValid()) {
400  dependencies_.clear();
401  return false;
402  }
403  dep->PrepareInstall();
404  }
405 
406  DisallowCodeDependencyChange no_dependency_change;
407  for (auto dep : dependencies_) {
408  // Check each dependency's validity again right before installing it,
409  // because the first iteration above might have invalidated some
410  // dependencies. For example, PrototypePropertyDependency::PrepareInstall
411  // can call EnsureHasInitialMap, which can invalidate a StableMapDependency
412  // on the prototype object's map.
413  if (!dep->IsValid()) {
414  dependencies_.clear();
415  return false;
416  }
417  dep->Install(MaybeObjectHandle::Weak(code));
418  }
419  SLOW_DCHECK(AreValid());
420 
421  dependencies_.clear();
422  return true;
423 }
424 
425 namespace {
426 // This function expects to never see a JSProxy.
427 void DependOnStablePrototypeChain(JSHeapBroker* broker,
428  CompilationDependencies* deps, MapRef map,
429  const JSObjectRef& last_prototype) {
430  while (true) {
431  map.SerializePrototype();
432  JSObjectRef proto = map.prototype().AsJSObject();
433  map = proto.map();
434  deps->DependOnStableMap(map);
435  if (proto.equals(last_prototype)) break;
436  }
437 }
438 } // namespace
439 
440 void CompilationDependencies::DependOnStablePrototypeChains(
441  JSHeapBroker* broker, std::vector<Handle<Map>> const& receiver_maps,
442  const JSObjectRef& holder) {
443  // Determine actual holder and perform prototype chain checks.
444  for (auto map : receiver_maps) {
445  MapRef receiver_map(broker, map);
446  if (receiver_map.IsPrimitiveMap()) {
447  // Perform the implicit ToObject for primitives here.
448  // Implemented according to ES6 section 7.3.2 GetV (V, P).
449  base::Optional<JSFunctionRef> constructor =
450  broker->native_context().GetConstructorFunction(receiver_map);
451  if (constructor.has_value()) receiver_map = constructor->initial_map();
452  }
453  DependOnStablePrototypeChain(broker, this, receiver_map, holder);
454  }
455 }
456 
457 void CompilationDependencies::DependOnElementsKinds(
458  const AllocationSiteRef& site) {
459  AllocationSiteRef current = site;
460  while (true) {
461  DependOnElementsKind(current);
462  if (!current.nested_site().IsAllocationSite()) break;
463  current = current.nested_site().AsAllocationSite();
464  }
465  CHECK_EQ(current.nested_site().AsSmi(), 0);
466 }
467 
468 SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map,
469  int instance_size)
470  : instance_size_(instance_size),
471  inobject_property_count_(
472  (instance_size >> kPointerSizeLog2) -
473  initial_map.GetInObjectPropertiesStartInWords()) {}
474 
475 SlackTrackingPrediction
476 CompilationDependencies::DependOnInitialMapInstanceSizePrediction(
477  const JSFunctionRef& function) {
478  MapRef initial_map = DependOnInitialMap(function);
479  int instance_size = function.InitialMapInstanceSizeWithMinSlack();
480  // Currently, we always install the prediction dependency. If this turns out
481  // to be too expensive, we can only install the dependency if slack
482  // tracking is active.
483  dependencies_.push_front(
484  new (zone_)
485  InitialMapInstanceSizePredictionDependency(function, instance_size));
486  DCHECK_LE(instance_size, function.initial_map().instance_size());
487  return SlackTrackingPrediction(initial_map, instance_size);
488 }
489 
490 } // namespace compiler
491 } // namespace internal
492 } // namespace v8
Definition: libplatform.h:13