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 :
10 : namespace v8 {
11 : namespace internal {
12 : namespace compiler {
13 :
14 474828 : CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone)
15 949656 : : zone_(zone), dependencies_(zone), isolate_(isolate) {}
16 :
17 875155 : class CompilationDependencies::Dependency : public ZoneObject {
18 : public:
19 : virtual bool IsValid() const = 0;
20 810960 : virtual void PrepareInstall() {}
21 : virtual void Install(const MaybeObjectHandle& code) = 0;
22 :
23 : #ifdef DEBUG
24 : virtual bool IsPretenureModeDependency() const { return false; }
25 : #endif
26 : };
27 :
28 : class InitialMapDependency final : public CompilationDependencies::Dependency {
29 : public:
30 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
31 : // longer need to explicitly store the initial map.
32 : InitialMapDependency(const JSFunctionRef& function, const MapRef& initial_map)
33 5404 : : function_(function), initial_map_(initial_map) {
34 : DCHECK(function_.has_initial_map());
35 : DCHECK(function_.initial_map().equals(initial_map_));
36 : }
37 :
38 10769 : bool IsValid() const override {
39 10769 : Handle<JSFunction> function = function_.object();
40 32307 : return function->has_initial_map() &&
41 43076 : function->initial_map() == *initial_map_.object();
42 : }
43 :
44 5384 : void Install(const MaybeObjectHandle& code) override {
45 : SLOW_DCHECK(IsValid());
46 : DependentCode::InstallDependency(function_.isolate(), code,
47 : initial_map_.object(),
48 10768 : DependentCode::kInitialMapChangedGroup);
49 5384 : }
50 :
51 : private:
52 : JSFunctionRef function_;
53 : MapRef initial_map_;
54 : };
55 :
56 : class PrototypePropertyDependency final
57 : : public CompilationDependencies::Dependency {
58 : public:
59 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
60 : // longer need to explicitly store the prototype.
61 : PrototypePropertyDependency(const JSFunctionRef& function,
62 : const ObjectRef& prototype)
63 56892 : : function_(function), prototype_(prototype) {
64 : DCHECK(function_.has_prototype());
65 : DCHECK(!function_.PrototypeRequiresRuntimeLookup());
66 : DCHECK(function_.prototype().equals(prototype_));
67 : }
68 :
69 113768 : bool IsValid() const override {
70 113768 : Handle<JSFunction> function = function_.object();
71 455072 : return function->has_prototype_slot() && function->has_prototype() &&
72 455072 : !function->PrototypeRequiresRuntimeLookup() &&
73 455072 : function->prototype() == *prototype_.object();
74 : }
75 :
76 56891 : void PrepareInstall() override {
77 : SLOW_DCHECK(IsValid());
78 56891 : Handle<JSFunction> function = function_.object();
79 56891 : if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function);
80 56891 : }
81 :
82 56877 : void Install(const MaybeObjectHandle& code) override {
83 : SLOW_DCHECK(IsValid());
84 56877 : Handle<JSFunction> function = function_.object();
85 : DCHECK(function->has_initial_map());
86 170631 : Handle<Map> initial_map(function->initial_map(), function_.isolate());
87 : DependentCode::InstallDependency(function_.isolate(), code, initial_map,
88 56877 : DependentCode::kInitialMapChangedGroup);
89 56877 : }
90 :
91 : private:
92 : JSFunctionRef function_;
93 : ObjectRef prototype_;
94 : };
95 :
96 : class StableMapDependency final : public CompilationDependencies::Dependency {
97 : public:
98 158680 : explicit StableMapDependency(const MapRef& map) : map_(map) {
99 : DCHECK(map_.is_stable());
100 : }
101 :
102 632886 : bool IsValid() const override { return map_.object()->is_stable(); }
103 :
104 158195 : void Install(const MaybeObjectHandle& code) override {
105 : SLOW_DCHECK(IsValid());
106 : DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
107 316390 : DependentCode::kPrototypeCheckGroup);
108 158195 : }
109 :
110 : private:
111 : MapRef map_;
112 : };
113 :
114 : class TransitionDependency final : public CompilationDependencies::Dependency {
115 : public:
116 15441 : explicit TransitionDependency(const MapRef& map) : map_(map) {
117 : DCHECK(!map_.is_deprecated());
118 : }
119 :
120 61676 : bool IsValid() const override { return !map_.object()->is_deprecated(); }
121 :
122 15417 : void Install(const MaybeObjectHandle& code) override {
123 : SLOW_DCHECK(IsValid());
124 : DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
125 30834 : DependentCode::kTransitionGroup);
126 15417 : }
127 :
128 : private:
129 : MapRef map_;
130 : };
131 :
132 : class PretenureModeDependency final
133 : : public CompilationDependencies::Dependency {
134 : public:
135 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
136 : // longer need to explicitly store the mode.
137 : PretenureModeDependency(const AllocationSiteRef& site, PretenureFlag mode)
138 7585 : : site_(site), mode_(mode) {
139 : DCHECK_EQ(mode_, site_.GetPretenureMode());
140 : }
141 :
142 15030 : bool IsValid() const override {
143 30060 : return mode_ == site_.object()->GetPretenureMode();
144 : }
145 :
146 7510 : void Install(const MaybeObjectHandle& code) override {
147 : SLOW_DCHECK(IsValid());
148 : DependentCode::InstallDependency(
149 : site_.isolate(), code, site_.object(),
150 15020 : DependentCode::kAllocationSiteTenuringChangedGroup);
151 7510 : }
152 :
153 : #ifdef DEBUG
154 : bool IsPretenureModeDependency() const override { return true; }
155 : #endif
156 :
157 : private:
158 : AllocationSiteRef site_;
159 : PretenureFlag mode_;
160 : };
161 :
162 : class FieldTypeDependency final : public CompilationDependencies::Dependency {
163 : public:
164 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
165 : // longer need to explicitly store the type.
166 : FieldTypeDependency(const MapRef& owner, int descriptor,
167 : const ObjectRef& type, PropertyConstness constness)
168 : : owner_(owner),
169 : descriptor_(descriptor),
170 : type_(type),
171 1864 : constness_(constness) {
172 : DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_)));
173 : DCHECK(type_.equals(owner_.GetFieldType(descriptor_)));
174 : DCHECK_EQ(constness_, owner_.GetPropertyDetails(descriptor_).constness());
175 : }
176 :
177 3382 : bool IsValid() const override {
178 : DisallowHeapAllocation no_heap_allocation;
179 3382 : Handle<Map> owner = owner_.object();
180 3382 : Handle<Object> type = type_.object();
181 16840 : return *type == owner->instance_descriptors()->GetFieldType(descriptor_) &&
182 10006 : constness_ == owner->instance_descriptors()
183 10006 : ->GetDetails(descriptor_)
184 10076 : .constness();
185 : }
186 :
187 1541 : void Install(const MaybeObjectHandle& code) override {
188 : SLOW_DCHECK(IsValid());
189 : DependentCode::InstallDependency(owner_.isolate(), code, owner_.object(),
190 3082 : DependentCode::kFieldOwnerGroup);
191 1541 : }
192 :
193 : private:
194 : MapRef owner_;
195 : int descriptor_;
196 : ObjectRef type_;
197 : PropertyConstness constness_;
198 : };
199 :
200 : class GlobalPropertyDependency final
201 : : public CompilationDependencies::Dependency {
202 : public:
203 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
204 : // longer need to explicitly store the type and the read_only flag.
205 : GlobalPropertyDependency(const PropertyCellRef& cell, PropertyCellType type,
206 : bool read_only)
207 601573 : : cell_(cell), type_(type), read_only_(read_only) {
208 : DCHECK_EQ(type_, cell_.property_details().cell_type());
209 : DCHECK_EQ(read_only_, cell_.property_details().IsReadOnly());
210 : }
211 :
212 1201380 : bool IsValid() const override {
213 1201380 : Handle<PropertyCell> cell = cell_.object();
214 : // The dependency is never valid if the cell is 'invalidated'. This is
215 : // marked by setting the value to the hole.
216 3604140 : if (cell->value() == *(cell_.isolate()->factory()->the_hole_value())) {
217 : DCHECK(cell->property_details().cell_type() ==
218 : PropertyCellType::kInvalidated ||
219 : cell->property_details().cell_type() ==
220 : PropertyCellType::kUninitialized);
221 : return false;
222 : }
223 6006856 : return type_ == cell->property_details().cell_type() &&
224 3604108 : read_only_ == cell->property_details().IsReadOnly();
225 : }
226 :
227 600656 : void Install(const MaybeObjectHandle& code) override {
228 : SLOW_DCHECK(IsValid());
229 : DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
230 1201312 : DependentCode::kPropertyCellChangedGroup);
231 600656 : }
232 :
233 : private:
234 : PropertyCellRef cell_;
235 : PropertyCellType type_;
236 : bool read_only_;
237 : };
238 :
239 : class ProtectorDependency final : public CompilationDependencies::Dependency {
240 : public:
241 19207 : explicit ProtectorDependency(const PropertyCellRef& cell) : cell_(cell) {
242 : DCHECK_EQ(cell_.value().AsSmi(), Isolate::kProtectorValid);
243 : }
244 :
245 38188 : bool IsValid() const override {
246 38188 : Handle<PropertyCell> cell = cell_.object();
247 76376 : return cell->value() == Smi::FromInt(Isolate::kProtectorValid);
248 : }
249 :
250 19092 : void Install(const MaybeObjectHandle& code) override {
251 : SLOW_DCHECK(IsValid());
252 : DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
253 38184 : DependentCode::kPropertyCellChangedGroup);
254 19092 : }
255 :
256 : private:
257 : PropertyCellRef cell_;
258 : };
259 :
260 : class ElementsKindDependency final
261 : : public CompilationDependencies::Dependency {
262 : public:
263 : // TODO(neis): Once the concurrent compiler frontend is always-on, we no
264 : // longer need to explicitly store the elements kind.
265 : ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind)
266 3105 : : site_(site), kind_(kind) {
267 : DCHECK(AllocationSite::ShouldTrack(kind_));
268 : DCHECK_EQ(kind_, site_.PointsToLiteral()
269 : ? site_.boilerplate().value().GetElementsKind()
270 : : site_.GetElementsKind());
271 : }
272 :
273 6143 : bool IsValid() const override {
274 6143 : Handle<AllocationSite> site = site_.object();
275 12286 : ElementsKind kind = site->PointsToLiteral()
276 10143 : ? site->boilerplate()->GetElementsKind()
277 18429 : : site->GetElementsKind();
278 6143 : return kind_ == kind;
279 : }
280 :
281 3065 : void Install(const MaybeObjectHandle& code) override {
282 : SLOW_DCHECK(IsValid());
283 : DependentCode::InstallDependency(
284 : site_.isolate(), code, site_.object(),
285 6130 : DependentCode::kAllocationSiteTransitionChangedGroup);
286 3065 : }
287 :
288 : private:
289 : AllocationSiteRef site_;
290 : ElementsKind kind_;
291 : };
292 :
293 : class InitialMapInstanceSizePredictionDependency final
294 : : public CompilationDependencies::Dependency {
295 : public:
296 : InitialMapInstanceSizePredictionDependency(const JSFunctionRef& function,
297 : int instance_size)
298 5404 : : function_(function), instance_size_(instance_size) {}
299 :
300 10771 : bool IsValid() const override {
301 : // The dependency is valid if the prediction is the same as the current
302 : // slack tracking result.
303 21542 : if (!function_.object()->has_initial_map()) return false;
304 21538 : int instance_size = function_.object()->ComputeInstanceSizeWithMinSlack(
305 21538 : function_.isolate());
306 10769 : return instance_size == instance_size_;
307 : }
308 :
309 5385 : void PrepareInstall() override {
310 : SLOW_DCHECK(IsValid());
311 10770 : function_.object()->CompleteInobjectSlackTrackingIfActive();
312 5385 : }
313 :
314 5384 : void Install(const MaybeObjectHandle& code) override {
315 : SLOW_DCHECK(IsValid());
316 : DCHECK(!function_.object()
317 : ->initial_map()
318 : ->IsInobjectSlackTrackingInProgress());
319 5384 : }
320 :
321 : private:
322 : JSFunctionRef function_;
323 : int instance_size_;
324 : };
325 :
326 5404 : MapRef CompilationDependencies::DependOnInitialMap(
327 : const JSFunctionRef& function) {
328 5404 : MapRef map = function.initial_map();
329 16212 : dependencies_.push_front(new (zone_) InitialMapDependency(function, map));
330 5404 : return map;
331 : }
332 :
333 56892 : ObjectRef CompilationDependencies::DependOnPrototypeProperty(
334 : const JSFunctionRef& function) {
335 56892 : ObjectRef prototype = function.prototype();
336 : dependencies_.push_front(
337 170676 : new (zone_) PrototypePropertyDependency(function, prototype));
338 56892 : return prototype;
339 : }
340 :
341 161346 : void CompilationDependencies::DependOnStableMap(const MapRef& map) {
342 161346 : if (map.CanTransition()) {
343 476041 : dependencies_.push_front(new (zone_) StableMapDependency(map));
344 : } else {
345 : DCHECK(map.is_stable());
346 : }
347 161347 : }
348 :
349 17873 : void CompilationDependencies::DependOnTransition(const MapRef& target_map) {
350 17873 : if (target_map.CanBeDeprecated()) {
351 46323 : dependencies_.push_front(new (zone_) TransitionDependency(target_map));
352 : } else {
353 : DCHECK(!target_map.is_deprecated());
354 : }
355 17873 : }
356 :
357 7585 : PretenureFlag CompilationDependencies::DependOnPretenureMode(
358 : const AllocationSiteRef& site) {
359 7585 : PretenureFlag mode = site.GetPretenureMode();
360 22755 : dependencies_.push_front(new (zone_) PretenureModeDependency(site, mode));
361 7585 : return mode;
362 : }
363 :
364 1864 : void CompilationDependencies::DependOnFieldType(const MapRef& map,
365 : int descriptor) {
366 1864 : MapRef owner = map.FindFieldOwner(descriptor);
367 1864 : ObjectRef type = owner.GetFieldType(descriptor);
368 : PropertyConstness constness =
369 1864 : owner.GetPropertyDetails(descriptor).constness();
370 : DCHECK(type.equals(map.GetFieldType(descriptor)));
371 : dependencies_.push_front(
372 5592 : new (zone_) FieldTypeDependency(owner, descriptor, type, constness));
373 1864 : }
374 :
375 601572 : void CompilationDependencies::DependOnGlobalProperty(
376 : const PropertyCellRef& cell) {
377 1203145 : PropertyCellType type = cell.property_details().cell_type();
378 1203147 : bool read_only = cell.property_details().IsReadOnly();
379 : dependencies_.push_front(new (zone_)
380 1804719 : GlobalPropertyDependency(cell, type, read_only));
381 601572 : }
382 :
383 19207 : void CompilationDependencies::DependOnProtector(const PropertyCellRef& cell) {
384 57621 : dependencies_.push_front(new (zone_) ProtectorDependency(cell));
385 19207 : }
386 :
387 7824 : void CompilationDependencies::DependOnElementsKind(
388 : const AllocationSiteRef& site) {
389 : // Do nothing if the object doesn't have any useful element transitions left.
390 7824 : ElementsKind kind = site.PointsToLiteral()
391 20242 : ? site.boilerplate().value().GetElementsKind()
392 15648 : : site.GetElementsKind();
393 7824 : if (AllocationSite::ShouldTrack(kind)) {
394 9315 : dependencies_.push_front(new (zone_) ElementsKindDependency(site, kind));
395 : }
396 7824 : }
397 :
398 300 : bool CompilationDependencies::AreValid() const {
399 830 : for (auto dep : dependencies_) {
400 300 : if (!dep->IsValid()) return false;
401 : }
402 : return true;
403 : }
404 :
405 455990 : bool CompilationDependencies::Commit(Handle<Code> code) {
406 1785216 : for (auto dep : dependencies_) {
407 873277 : if (!dep->IsValid()) {
408 : dependencies_.clear();
409 : return false;
410 : }
411 873236 : dep->PrepareInstall();
412 : }
413 :
414 : DisallowCodeDependencyChange no_dependency_change;
415 1785019 : for (auto dep : dependencies_) {
416 : // Check each dependency's validity again right before installing it,
417 : // because the first iteration above might have invalidated some
418 : // dependencies. For example, PrototypePropertyDependency::PrepareInstall
419 : // can call EnsureHasInitialMap, which can invalidate a StableMapDependency
420 : // on the prototype object's map.
421 873135 : if (!dep->IsValid()) {
422 : dependencies_.clear();
423 : return false;
424 : }
425 873121 : dep->Install(MaybeObjectHandle::Weak(code));
426 : }
427 :
428 : // It is even possible that a GC during the above installations invalidated
429 : // one of the dependencies. However, this should only affect pretenure mode
430 : // dependencies, which we assert below. It is safe to return successfully in
431 : // these cases, because once the code gets executed it will do a stack check
432 : // that triggers its deoptimization.
433 455935 : if (FLAG_stress_gc_during_compilation) {
434 : isolate_->heap()->PreciseCollectAllGarbage(
435 : Heap::kNoGCFlags, GarbageCollectionReason::kTesting,
436 5 : kGCCallbackFlagForced);
437 : }
438 : #ifdef DEBUG
439 : for (auto dep : dependencies_) {
440 : CHECK_IMPLIES(!dep->IsValid(), dep->IsPretenureModeDependency());
441 : }
442 : #endif
443 :
444 : dependencies_.clear();
445 455935 : return true;
446 : }
447 :
448 : namespace {
449 : // This function expects to never see a JSProxy.
450 55148 : void DependOnStablePrototypeChain(JSHeapBroker* broker,
451 : CompilationDependencies* deps, MapRef map,
452 : const JSObjectRef& last_prototype) {
453 : while (true) {
454 82837 : map.SerializePrototype();
455 82838 : JSObjectRef proto = map.prototype().AsJSObject();
456 82838 : map = proto.map();
457 82837 : deps->DependOnStableMap(map);
458 82838 : if (proto.equals(last_prototype)) break;
459 : }
460 55149 : }
461 : } // namespace
462 :
463 52818 : void CompilationDependencies::DependOnStablePrototypeChains(
464 : JSHeapBroker* broker, std::vector<Handle<Map>> const& receiver_maps,
465 : const JSObjectRef& holder) {
466 : // Determine actual holder and perform prototype chain checks.
467 160785 : for (auto map : receiver_maps) {
468 : MapRef receiver_map(broker, map);
469 55148 : if (receiver_map.IsPrimitiveMap()) {
470 : // Perform the implicit ToObject for primitives here.
471 : // Implemented according to ES6 section 7.3.2 GetV (V, P).
472 : base::Optional<JSFunctionRef> constructor =
473 8546 : broker->native_context().GetConstructorFunction(receiver_map);
474 8546 : if (constructor.has_value()) receiver_map = constructor->initial_map();
475 : }
476 55149 : DependOnStablePrototypeChain(broker, this, receiver_map, holder);
477 : }
478 52819 : }
479 :
480 5970 : void CompilationDependencies::DependOnElementsKinds(
481 : const AllocationSiteRef& site) {
482 5970 : AllocationSiteRef current = site;
483 : while (true) {
484 6209 : DependOnElementsKind(current);
485 6209 : if (!current.nested_site().IsAllocationSite()) break;
486 239 : current = current.nested_site().AsAllocationSite();
487 : }
488 239 : CHECK_EQ(current.nested_site().AsSmi(), 0);
489 5970 : }
490 :
491 1062 : SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map,
492 : int instance_size)
493 : : instance_size_(instance_size),
494 : inobject_property_count_(
495 12932 : (instance_size >> kTaggedSizeLog2) -
496 8590 : initial_map.GetInObjectPropertiesStartInWords()) {}
497 :
498 : SlackTrackingPrediction
499 5404 : CompilationDependencies::DependOnInitialMapInstanceSizePrediction(
500 : const JSFunctionRef& function) {
501 5404 : MapRef initial_map = DependOnInitialMap(function);
502 5404 : int instance_size = function.InitialMapInstanceSizeWithMinSlack();
503 : // Currently, we always install the prediction dependency. If this turns out
504 : // to be too expensive, we can only install the dependency if slack
505 : // tracking is active.
506 : dependencies_.push_front(
507 : new (zone_)
508 16212 : InitialMapInstanceSizePredictionDependency(function, instance_size));
509 : DCHECK_LE(instance_size, function.initial_map().instance_size());
510 5404 : return SlackTrackingPrediction(initial_map, instance_size);
511 : }
512 :
513 : } // namespace compiler
514 : } // namespace internal
515 183867 : } // namespace v8
|