Line data Source code
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 : #include "src/objects/allocation-site-inl.h"
10 :
11 : namespace v8 {
12 : namespace internal {
13 : namespace compiler {
14 :
15 475817 : CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone)
16 951634 : : zone_(zone), dependencies_(zone), isolate_(isolate) {}
17 :
18 484220 : class CompilationDependencies::Dependency : public ZoneObject {
19 : public:
20 : virtual bool IsValid() const = 0;
21 475571 : virtual void PrepareInstall() {}
22 : virtual void Install(const MaybeObjectHandle& code) = 0;
23 :
24 : #ifdef DEBUG
25 : virtual bool IsPretenureModeDependency() const { return false; }
26 : #endif
27 : };
28 :
29 : class InitialMapDependency final : public CompilationDependencies::Dependency {
30 : public:
31 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
32 : // longer need to explicitly store the initial map.
33 : InitialMapDependency(const JSFunctionRef& function, const MapRef& initial_map)
34 5276 : : function_(function), initial_map_(initial_map) {
35 : DCHECK(function_.has_initial_map());
36 : DCHECK(function_.initial_map().equals(initial_map_));
37 : }
38 :
39 10504 : bool IsValid() const override {
40 10504 : Handle<JSFunction> function = function_.object();
41 31512 : return function->has_initial_map() &&
42 42016 : function->initial_map() == *initial_map_.object();
43 : }
44 :
45 5252 : void Install(const MaybeObjectHandle& code) override {
46 : SLOW_DCHECK(IsValid());
47 : DependentCode::InstallDependency(function_.isolate(), code,
48 : initial_map_.object(),
49 10504 : DependentCode::kInitialMapChangedGroup);
50 5252 : }
51 :
52 : private:
53 : JSFunctionRef function_;
54 : MapRef initial_map_;
55 : };
56 :
57 : class PrototypePropertyDependency final
58 : : public CompilationDependencies::Dependency {
59 : public:
60 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
61 : // longer need to explicitly store the prototype.
62 : PrototypePropertyDependency(const JSFunctionRef& function,
63 : const ObjectRef& prototype)
64 1376 : : function_(function), prototype_(prototype) {
65 : DCHECK(function_.has_prototype());
66 : DCHECK(!function_.PrototypeRequiresRuntimeLookup());
67 : DCHECK(function_.prototype().equals(prototype_));
68 : }
69 :
70 2738 : bool IsValid() const override {
71 2738 : Handle<JSFunction> function = function_.object();
72 10952 : return function->has_prototype_slot() && function->has_prototype() &&
73 10952 : !function->PrototypeRequiresRuntimeLookup() &&
74 10952 : function->prototype() == *prototype_.object();
75 : }
76 :
77 1376 : void PrepareInstall() override {
78 : SLOW_DCHECK(IsValid());
79 1376 : Handle<JSFunction> function = function_.object();
80 1376 : if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function);
81 1376 : }
82 :
83 1362 : void Install(const MaybeObjectHandle& code) override {
84 : SLOW_DCHECK(IsValid());
85 1362 : Handle<JSFunction> function = function_.object();
86 : DCHECK(function->has_initial_map());
87 4086 : Handle<Map> initial_map(function->initial_map(), function_.isolate());
88 : DependentCode::InstallDependency(function_.isolate(), code, initial_map,
89 1362 : DependentCode::kInitialMapChangedGroup);
90 1362 : }
91 :
92 : private:
93 : JSFunctionRef function_;
94 : ObjectRef prototype_;
95 : };
96 :
97 : class StableMapDependency final : public CompilationDependencies::Dependency {
98 : public:
99 166185 : explicit StableMapDependency(const MapRef& map) : map_(map) {
100 : DCHECK(map_.is_stable());
101 : }
102 :
103 662626 : bool IsValid() const override { return map_.object()->is_stable(); }
104 :
105 165628 : void Install(const MaybeObjectHandle& code) override {
106 : SLOW_DCHECK(IsValid());
107 : DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
108 331256 : DependentCode::kPrototypeCheckGroup);
109 165628 : }
110 :
111 : private:
112 : MapRef map_;
113 : };
114 :
115 : class TransitionDependency final : public CompilationDependencies::Dependency {
116 : public:
117 19384 : explicit TransitionDependency(const MapRef& map) : map_(map) {
118 : DCHECK(!map_.is_deprecated());
119 : }
120 :
121 77412 : bool IsValid() const override { return !map_.object()->is_deprecated(); }
122 :
123 19351 : void Install(const MaybeObjectHandle& code) override {
124 : SLOW_DCHECK(IsValid());
125 : DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
126 38702 : DependentCode::kTransitionGroup);
127 19351 : }
128 :
129 : private:
130 : MapRef map_;
131 : };
132 :
133 : class PretenureModeDependency final
134 : : public CompilationDependencies::Dependency {
135 : public:
136 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
137 : // longer need to explicitly store the mode.
138 : PretenureModeDependency(const AllocationSiteRef& site, PretenureFlag mode)
139 7312 : : site_(site), mode_(mode) {
140 : DCHECK_EQ(mode_, site_.GetPretenureMode());
141 : }
142 :
143 14438 : bool IsValid() const override {
144 28876 : return mode_ == site_.object()->GetPretenureMode();
145 : }
146 :
147 7213 : void Install(const MaybeObjectHandle& code) override {
148 : SLOW_DCHECK(IsValid());
149 : DependentCode::InstallDependency(
150 : site_.isolate(), code, site_.object(),
151 14426 : DependentCode::kAllocationSiteTenuringChangedGroup);
152 7213 : }
153 :
154 : #ifdef DEBUG
155 : bool IsPretenureModeDependency() const override { return true; }
156 : #endif
157 :
158 : private:
159 : AllocationSiteRef site_;
160 : PretenureFlag mode_;
161 : };
162 :
163 : class FieldTypeDependency final : public CompilationDependencies::Dependency {
164 : public:
165 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
166 : // longer need to explicitly store the type.
167 : FieldTypeDependency(const MapRef& owner, int descriptor,
168 : const ObjectRef& type)
169 3440 : : owner_(owner), descriptor_(descriptor), type_(type) {
170 : DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_)));
171 : DCHECK(type_.equals(owner_.GetFieldType(descriptor_)));
172 : }
173 :
174 6361 : bool IsValid() const override {
175 : DisallowHeapAllocation no_heap_allocation;
176 6361 : Handle<Map> owner = owner_.object();
177 6361 : Handle<Object> type = type_.object();
178 19083 : return *type == owner->instance_descriptors()->GetFieldType(descriptor_);
179 : }
180 :
181 2958 : void Install(const MaybeObjectHandle& code) override {
182 : SLOW_DCHECK(IsValid());
183 : DependentCode::InstallDependency(owner_.isolate(), code, owner_.object(),
184 5916 : DependentCode::kFieldOwnerGroup);
185 2958 : }
186 :
187 : private:
188 : MapRef owner_;
189 : int descriptor_;
190 : ObjectRef type_;
191 : };
192 :
193 : class FieldConstnessDependency final
194 : : public CompilationDependencies::Dependency {
195 : public:
196 : FieldConstnessDependency(const MapRef& owner, int descriptor)
197 71599 : : owner_(owner), descriptor_(descriptor) {
198 : DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_)));
199 : DCHECK_EQ(PropertyConstness::kConst,
200 : owner_.GetPropertyDetails(descriptor_).constness());
201 : }
202 :
203 142821 : bool IsValid() const override {
204 : DisallowHeapAllocation no_heap_allocation;
205 142821 : Handle<Map> owner = owner_.object();
206 : return PropertyConstness::kConst ==
207 428463 : owner->instance_descriptors()->GetDetails(descriptor_).constness();
208 : }
209 :
210 71386 : void Install(const MaybeObjectHandle& code) override {
211 : SLOW_DCHECK(IsValid());
212 : DependentCode::InstallDependency(owner_.isolate(), code, owner_.object(),
213 142772 : DependentCode::kFieldOwnerGroup);
214 71386 : }
215 :
216 : private:
217 : MapRef owner_;
218 : int descriptor_;
219 : };
220 :
221 : class GlobalPropertyDependency final
222 : : public CompilationDependencies::Dependency {
223 : public:
224 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
225 : // longer need to explicitly store the type and the read_only flag.
226 : GlobalPropertyDependency(const PropertyCellRef& cell, PropertyCellType type,
227 : bool read_only)
228 182339 : : cell_(cell), type_(type), read_only_(read_only) {
229 : DCHECK_EQ(type_, cell_.property_details().cell_type());
230 : DCHECK_EQ(read_only_, cell_.property_details().IsReadOnly());
231 : }
232 :
233 363489 : bool IsValid() const override {
234 363489 : Handle<PropertyCell> cell = cell_.object();
235 : // The dependency is never valid if the cell is 'invalidated'. This is
236 : // marked by setting the value to the hole.
237 726978 : if (cell->value() == *(cell_.isolate()->factory()->the_hole_value())) {
238 : DCHECK(cell->property_details().cell_type() ==
239 : PropertyCellType::kInvalidated ||
240 : cell->property_details().cell_type() ==
241 : PropertyCellType::kUninitialized);
242 : return false;
243 : }
244 1817400 : return type_ == cell->property_details().cell_type() &&
245 1090433 : read_only_ == cell->property_details().IsReadOnly();
246 : }
247 :
248 181706 : void Install(const MaybeObjectHandle& code) override {
249 : SLOW_DCHECK(IsValid());
250 : DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
251 363412 : DependentCode::kPropertyCellChangedGroup);
252 181706 : }
253 :
254 : private:
255 : PropertyCellRef cell_;
256 : PropertyCellType type_;
257 : bool read_only_;
258 : };
259 :
260 : class ProtectorDependency final : public CompilationDependencies::Dependency {
261 : public:
262 19034 : explicit ProtectorDependency(const PropertyCellRef& cell) : cell_(cell) {
263 : DCHECK_EQ(cell_.value().AsSmi(), Isolate::kProtectorValid);
264 : }
265 :
266 38018 : bool IsValid() const override {
267 38018 : Handle<PropertyCell> cell = cell_.object();
268 38018 : return cell->value() == Smi::FromInt(Isolate::kProtectorValid);
269 : }
270 :
271 19007 : void Install(const MaybeObjectHandle& code) override {
272 : SLOW_DCHECK(IsValid());
273 : DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
274 38014 : DependentCode::kPropertyCellChangedGroup);
275 19007 : }
276 :
277 : private:
278 : PropertyCellRef cell_;
279 : };
280 :
281 : class ElementsKindDependency final
282 : : public CompilationDependencies::Dependency {
283 : public:
284 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
285 : // longer need to explicitly store the elements kind.
286 : ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind)
287 2999 : : site_(site), kind_(kind) {
288 : DCHECK(AllocationSite::ShouldTrack(kind_));
289 : DCHECK_EQ(kind_, site_.PointsToLiteral()
290 : ? site_.boilerplate().value().GetElementsKind()
291 : : site_.GetElementsKind());
292 : }
293 :
294 5924 : bool IsValid() const override {
295 5924 : Handle<AllocationSite> site = site_.object();
296 11848 : ElementsKind kind = site->PointsToLiteral()
297 9758 : ? site->boilerplate()->GetElementsKind()
298 17772 : : site->GetElementsKind();
299 5924 : return kind_ == kind;
300 : }
301 :
302 2954 : void Install(const MaybeObjectHandle& code) override {
303 : SLOW_DCHECK(IsValid());
304 : DependentCode::InstallDependency(
305 : site_.isolate(), code, site_.object(),
306 5908 : DependentCode::kAllocationSiteTransitionChangedGroup);
307 2954 : }
308 :
309 : private:
310 : AllocationSiteRef site_;
311 : ElementsKind kind_;
312 : };
313 :
314 : class InitialMapInstanceSizePredictionDependency final
315 : : public CompilationDependencies::Dependency {
316 : public:
317 : InitialMapInstanceSizePredictionDependency(const JSFunctionRef& function,
318 : int instance_size)
319 5276 : : function_(function), instance_size_(instance_size) {}
320 :
321 10509 : bool IsValid() const override {
322 : // The dependency is valid if the prediction is the same as the current
323 : // slack tracking result.
324 21018 : if (!function_.object()->has_initial_map()) return false;
325 21008 : int instance_size = function_.object()->ComputeInstanceSizeWithMinSlack(
326 21008 : function_.isolate());
327 10504 : return instance_size == instance_size_;
328 : }
329 :
330 5252 : void PrepareInstall() override {
331 : SLOW_DCHECK(IsValid());
332 10504 : function_.object()->CompleteInobjectSlackTrackingIfActive();
333 5252 : }
334 :
335 5252 : void Install(const MaybeObjectHandle& code) override {
336 : SLOW_DCHECK(IsValid());
337 : DCHECK(!function_.object()
338 : ->initial_map()
339 : ->IsInobjectSlackTrackingInProgress());
340 5252 : }
341 :
342 : private:
343 : JSFunctionRef function_;
344 : int instance_size_;
345 : };
346 :
347 5276 : MapRef CompilationDependencies::DependOnInitialMap(
348 : const JSFunctionRef& function) {
349 5276 : MapRef map = function.initial_map();
350 15828 : dependencies_.push_front(new (zone_) InitialMapDependency(function, map));
351 5276 : return map;
352 : }
353 :
354 1376 : ObjectRef CompilationDependencies::DependOnPrototypeProperty(
355 : const JSFunctionRef& function) {
356 1376 : ObjectRef prototype = function.prototype();
357 : dependencies_.push_front(
358 4128 : new (zone_) PrototypePropertyDependency(function, prototype));
359 1376 : return prototype;
360 : }
361 :
362 168045 : void CompilationDependencies::DependOnStableMap(const MapRef& map) {
363 168045 : if (map.CanTransition()) {
364 498555 : dependencies_.push_front(new (zone_) StableMapDependency(map));
365 : } else {
366 : DCHECK(map.is_stable());
367 : }
368 168045 : }
369 :
370 21771 : void CompilationDependencies::DependOnTransition(const MapRef& target_map) {
371 21771 : if (target_map.CanBeDeprecated()) {
372 58152 : dependencies_.push_front(new (zone_) TransitionDependency(target_map));
373 : } else {
374 : DCHECK(!target_map.is_deprecated());
375 : }
376 21771 : }
377 :
378 7312 : PretenureFlag CompilationDependencies::DependOnPretenureMode(
379 : const AllocationSiteRef& site) {
380 7312 : PretenureFlag mode = site.GetPretenureMode();
381 21936 : dependencies_.push_front(new (zone_) PretenureModeDependency(site, mode));
382 7312 : return mode;
383 : }
384 :
385 71639 : PropertyConstness CompilationDependencies::DependOnFieldConstness(
386 : const MapRef& map, int descriptor) {
387 71639 : MapRef owner = map.FindFieldOwner(descriptor);
388 : PropertyConstness constness =
389 143278 : owner.GetPropertyDetails(descriptor).constness();
390 71639 : if (constness == PropertyConstness::kMutable) return constness;
391 :
392 : // If the map can have fast elements transitions, then the field can be only
393 : // considered constant if the map does not transition.
394 143198 : if (Map::CanHaveFastTransitionableElementsKind(map.instance_type())) {
395 : // If the map can already transition away, let us report the field as
396 : // mutable.
397 15583 : if (!map.is_stable()) {
398 : return PropertyConstness::kMutable;
399 : }
400 15583 : DependOnStableMap(map);
401 : }
402 :
403 : DCHECK_EQ(constness, PropertyConstness::kConst);
404 : dependencies_.push_front(new (zone_)
405 214797 : FieldConstnessDependency(owner, descriptor));
406 71599 : return PropertyConstness::kConst;
407 : }
408 :
409 3440 : void CompilationDependencies::DependOnFieldType(const MapRef& map,
410 : int descriptor) {
411 3440 : MapRef owner = map.FindFieldOwner(descriptor);
412 3440 : ObjectRef type = owner.GetFieldType(descriptor);
413 : DCHECK(type.equals(map.GetFieldType(descriptor)));
414 : dependencies_.push_front(new (zone_)
415 10320 : FieldTypeDependency(owner, descriptor, type));
416 3440 : }
417 :
418 182339 : void CompilationDependencies::DependOnGlobalProperty(
419 : const PropertyCellRef& cell) {
420 364678 : PropertyCellType type = cell.property_details().cell_type();
421 364678 : bool read_only = cell.property_details().IsReadOnly();
422 : dependencies_.push_front(new (zone_)
423 547017 : GlobalPropertyDependency(cell, type, read_only));
424 182339 : }
425 :
426 19034 : void CompilationDependencies::DependOnProtector(const PropertyCellRef& cell) {
427 57102 : dependencies_.push_front(new (zone_) ProtectorDependency(cell));
428 19034 : }
429 :
430 7536 : void CompilationDependencies::DependOnElementsKind(
431 : const AllocationSiteRef& site) {
432 : // Do nothing if the object doesn't have any useful element transitions left.
433 7536 : ElementsKind kind = site.PointsToLiteral()
434 19486 : ? site.boilerplate().value().GetElementsKind()
435 15072 : : site.GetElementsKind();
436 7536 : if (AllocationSite::ShouldTrack(kind)) {
437 8997 : dependencies_.push_front(new (zone_) ElementsKindDependency(site, kind));
438 : }
439 7536 : }
440 :
441 465 : bool CompilationDependencies::AreValid() const {
442 1325 : for (auto dep : dependencies_) {
443 495 : if (!dep->IsValid()) return false;
444 : }
445 : return true;
446 : }
447 :
448 456574 : bool CompilationDependencies::Commit(Handle<Code> code) {
449 1395347 : for (auto dep : dependencies_) {
450 482243 : if (!dep->IsValid()) {
451 : dependencies_.clear();
452 : return false;
453 : }
454 482199 : dep->PrepareInstall();
455 : }
456 :
457 : DisallowCodeDependencyChange no_dependency_change;
458 1395129 : for (auto dep : dependencies_) {
459 : // Check each dependency's validity again right before installing it,
460 : // because the first iteration above might have invalidated some
461 : // dependencies. For example, PrototypePropertyDependency::PrepareInstall
462 : // can call EnsureHasInitialMap, which can invalidate a StableMapDependency
463 : // on the prototype object's map.
464 482083 : if (!dep->IsValid()) {
465 : dependencies_.clear();
466 : return false;
467 : }
468 482069 : dep->Install(MaybeObjectHandle::Weak(code));
469 : }
470 :
471 : // It is even possible that a GC during the above installations invalidated
472 : // one of the dependencies. However, this should only affect pretenure mode
473 : // dependencies, which we assert below. It is safe to return successfully in
474 : // these cases, because once the code gets executed it will do a stack check
475 : // that triggers its deoptimization.
476 456516 : if (FLAG_stress_gc_during_compilation) {
477 : isolate_->heap()->PreciseCollectAllGarbage(
478 : Heap::kNoGCFlags, GarbageCollectionReason::kTesting,
479 5 : kGCCallbackFlagForced);
480 : }
481 : #ifdef DEBUG
482 : for (auto dep : dependencies_) {
483 : CHECK_IMPLIES(!dep->IsValid(), dep->IsPretenureModeDependency());
484 : }
485 : #endif
486 :
487 : dependencies_.clear();
488 456516 : return true;
489 : }
490 :
491 : namespace {
492 : // This function expects to never see a JSProxy.
493 57985 : void DependOnStablePrototypeChain(JSHeapBroker* broker,
494 : CompilationDependencies* deps, MapRef map,
495 : const JSObjectRef& last_prototype) {
496 : while (true) {
497 89379 : map.SerializePrototype();
498 89379 : JSObjectRef proto = map.prototype().AsJSObject();
499 89379 : map = proto.map();
500 89379 : deps->DependOnStableMap(map);
501 89379 : if (proto.equals(last_prototype)) break;
502 : }
503 57985 : }
504 : } // namespace
505 :
506 55408 : void CompilationDependencies::DependOnStablePrototypeChains(
507 : JSHeapBroker* broker, std::vector<Handle<Map>> const& receiver_maps,
508 : const JSObjectRef& holder) {
509 : // Determine actual holder and perform prototype chain checks.
510 168801 : for (auto map : receiver_maps) {
511 : MapRef receiver_map(broker, map);
512 57985 : if (receiver_map.IsPrimitiveMap()) {
513 : // Perform the implicit ToObject for primitives here.
514 : // Implemented according to ES6 section 7.3.2 GetV (V, P).
515 : base::Optional<JSFunctionRef> constructor =
516 7991 : broker->native_context().GetConstructorFunction(receiver_map);
517 7991 : if (constructor.has_value()) receiver_map = constructor->initial_map();
518 : }
519 57985 : DependOnStablePrototypeChain(broker, this, receiver_map, holder);
520 : }
521 55408 : }
522 :
523 5751 : void CompilationDependencies::DependOnElementsKinds(
524 : const AllocationSiteRef& site) {
525 5751 : AllocationSiteRef current = site;
526 : while (true) {
527 5975 : DependOnElementsKind(current);
528 5975 : if (!current.nested_site().IsAllocationSite()) break;
529 224 : current = current.nested_site().AsAllocationSite();
530 : }
531 224 : CHECK_EQ(current.nested_site().AsSmi(), 0);
532 5751 : }
533 :
534 1024 : SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map,
535 : int instance_size)
536 : : instance_size_(instance_size),
537 : inobject_property_count_(
538 12600 : (instance_size >> kTaggedSizeLog2) -
539 8348 : initial_map.GetInObjectPropertiesStartInWords()) {}
540 :
541 : SlackTrackingPrediction
542 5276 : CompilationDependencies::DependOnInitialMapInstanceSizePrediction(
543 : const JSFunctionRef& function) {
544 5276 : MapRef initial_map = DependOnInitialMap(function);
545 5276 : int instance_size = function.InitialMapInstanceSizeWithMinSlack();
546 : // Currently, we always install the prediction dependency. If this turns out
547 : // to be too expensive, we can only install the dependency if slack
548 : // tracking is active.
549 : dependencies_.push_front(
550 : new (zone_)
551 15828 : InitialMapInstanceSizePredictionDependency(function, instance_size));
552 : DCHECK_LE(instance_size, function.initial_map().instance_size());
553 5276 : return SlackTrackingPrediction(initial_map, instance_size);
554 : }
555 :
556 : } // namespace compiler
557 : } // namespace internal
558 178779 : } // namespace v8
|