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
CompilationDependencies(Isolate * isolate,Zone * zone)14 CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone)
15 : zone_(zone), dependencies_(zone) {}
16
17 class CompilationDependencies::Dependency : public ZoneObject {
18 public:
19 virtual bool IsValid() const = 0;
20 virtual void Install(MaybeObjectHandle code) = 0;
21 };
22
23 class InitialMapDependency final : public CompilationDependencies::Dependency {
24 public:
25 // TODO(neis): Once the concurrent compiler frontend is always-on, we no
26 // longer need to explicitly store the initial map.
InitialMapDependency(const JSFunctionRef & function,const MapRef & initial_map)27 InitialMapDependency(const JSFunctionRef& function, const MapRef& initial_map)
28 : function_(function), initial_map_(initial_map) {
29 DCHECK(function_.has_initial_map());
30 DCHECK(function_.initial_map().equals(initial_map_));
31 }
32
IsValid() const33 bool IsValid() const override {
34 Handle<JSFunction> function = function_.object<JSFunction>();
35 return function->has_initial_map() &&
36 function->initial_map() == *initial_map_.object<Map>();
37 }
38
Install(MaybeObjectHandle code)39 void Install(MaybeObjectHandle code) override {
40 SLOW_DCHECK(IsValid());
41 DependentCode::InstallDependency(function_.isolate(), code,
42 initial_map_.object<Map>(),
43 DependentCode::kInitialMapChangedGroup);
44 }
45
46 private:
47 JSFunctionRef function_;
48 MapRef initial_map_;
49 };
50
51 class PrototypePropertyDependency final
52 : public CompilationDependencies::Dependency {
53 public:
54 // TODO(neis): Once the concurrent compiler frontend is always-on, we no
55 // longer need to explicitly store the prototype.
PrototypePropertyDependency(const JSFunctionRef & function,const ObjectRef & prototype)56 PrototypePropertyDependency(const JSFunctionRef& function,
57 const ObjectRef& prototype)
58 : function_(function), prototype_(prototype) {
59 DCHECK(function_.has_prototype());
60 DCHECK(!function_.PrototypeRequiresRuntimeLookup());
61 DCHECK(function_.prototype().equals(prototype_));
62 }
63
IsValid() const64 bool IsValid() const override {
65 Handle<JSFunction> function = function_.object<JSFunction>();
66 return function->has_prototype_slot() && function->has_prototype() &&
67 !function->PrototypeRequiresRuntimeLookup() &&
68 function->prototype() == *prototype_.object();
69 }
70
Install(MaybeObjectHandle code)71 void Install(MaybeObjectHandle code) override {
72 SLOW_DCHECK(IsValid());
73 Handle<JSFunction> function = function_.object<JSFunction>();
74 if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function);
75 Handle<Map> initial_map(function->initial_map(), function_.isolate());
76 DependentCode::InstallDependency(function_.isolate(), code, initial_map,
77 DependentCode::kInitialMapChangedGroup);
78 }
79
80 private:
81 JSFunctionRef function_;
82 ObjectRef prototype_;
83 };
84
85 class StableMapDependency final : public CompilationDependencies::Dependency {
86 public:
StableMapDependency(const MapRef & map)87 explicit StableMapDependency(const MapRef& map) : map_(map) {
88 DCHECK(map_.is_stable());
89 }
90
IsValid() const91 bool IsValid() const override { return map_.object<Map>()->is_stable(); }
92
Install(MaybeObjectHandle code)93 void Install(MaybeObjectHandle code) override {
94 SLOW_DCHECK(IsValid());
95 DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(),
96 DependentCode::kPrototypeCheckGroup);
97 }
98
99 private:
100 MapRef map_;
101 };
102
103 class TransitionDependency final : public CompilationDependencies::Dependency {
104 public:
TransitionDependency(const MapRef & map)105 explicit TransitionDependency(const MapRef& map) : map_(map) {
106 DCHECK(!map_.is_deprecated());
107 }
108
IsValid() const109 bool IsValid() const override { return !map_.object<Map>()->is_deprecated(); }
110
Install(MaybeObjectHandle code)111 void Install(MaybeObjectHandle code) override {
112 SLOW_DCHECK(IsValid());
113 DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(),
114 DependentCode::kTransitionGroup);
115 }
116
117 private:
118 MapRef map_;
119 };
120
121 class PretenureModeDependency final
122 : public CompilationDependencies::Dependency {
123 public:
124 // TODO(neis): Once the concurrent compiler frontend is always-on, we no
125 // longer need to explicitly store the mode.
PretenureModeDependency(const AllocationSiteRef & site,PretenureFlag mode)126 PretenureModeDependency(const AllocationSiteRef& site, PretenureFlag mode)
127 : site_(site), mode_(mode) {
128 DCHECK_EQ(mode_, site_.GetPretenureMode());
129 }
130
IsValid() const131 bool IsValid() const override {
132 return mode_ == site_.object<AllocationSite>()->GetPretenureMode();
133 }
134
Install(MaybeObjectHandle code)135 void Install(MaybeObjectHandle code) override {
136 SLOW_DCHECK(IsValid());
137 DependentCode::InstallDependency(
138 site_.isolate(), code, site_.object<AllocationSite>(),
139 DependentCode::kAllocationSiteTenuringChangedGroup);
140 }
141
142 private:
143 AllocationSiteRef site_;
144 PretenureFlag mode_;
145 };
146
147 class FieldTypeDependency final : public CompilationDependencies::Dependency {
148 public:
149 // TODO(neis): Once the concurrent compiler frontend is always-on, we no
150 // longer need to explicitly store the type.
FieldTypeDependency(const MapRef & owner,int descriptor,const ObjectRef & type)151 FieldTypeDependency(const MapRef& owner, int descriptor,
152 const ObjectRef& type)
153 : owner_(owner), descriptor_(descriptor), type_(type) {
154 DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_)));
155 DCHECK(type_.equals(owner_.GetFieldType(descriptor_)));
156 }
157
IsValid() const158 bool IsValid() const override {
159 DisallowHeapAllocation no_heap_allocation;
160 Handle<Map> owner = owner_.object<Map>();
161 Handle<FieldType> type = type_.object<FieldType>();
162 return *type == owner->instance_descriptors()->GetFieldType(descriptor_);
163 }
164
Install(MaybeObjectHandle code)165 void Install(MaybeObjectHandle code) override {
166 SLOW_DCHECK(IsValid());
167 DependentCode::InstallDependency(owner_.isolate(), code,
168 owner_.object<Map>(),
169 DependentCode::kFieldOwnerGroup);
170 }
171
172 private:
173 MapRef owner_;
174 int descriptor_;
175 ObjectRef type_;
176 };
177
178 class GlobalPropertyDependency final
179 : public CompilationDependencies::Dependency {
180 public:
181 // TODO(neis): Once the concurrent compiler frontend is always-on, we no
182 // longer need to explicitly store the type and the read_only flag.
GlobalPropertyDependency(const PropertyCellRef & cell,PropertyCellType type,bool read_only)183 GlobalPropertyDependency(const PropertyCellRef& cell, PropertyCellType type,
184 bool read_only)
185 : cell_(cell), type_(type), read_only_(read_only) {
186 DCHECK_EQ(type_, cell_.property_details().cell_type());
187 DCHECK_EQ(read_only_, cell_.property_details().IsReadOnly());
188 }
189
IsValid() const190 bool IsValid() const override {
191 Handle<PropertyCell> cell = cell_.object<PropertyCell>();
192 return type_ == cell->property_details().cell_type() &&
193 read_only_ == cell->property_details().IsReadOnly();
194 }
195
Install(MaybeObjectHandle code)196 void Install(MaybeObjectHandle code) override {
197 SLOW_DCHECK(IsValid());
198 DependentCode::InstallDependency(cell_.isolate(), code,
199 cell_.object<PropertyCell>(),
200 DependentCode::kPropertyCellChangedGroup);
201 }
202
203 private:
204 PropertyCellRef cell_;
205 PropertyCellType type_;
206 bool read_only_;
207 };
208
209 class ProtectorDependency final : public CompilationDependencies::Dependency {
210 public:
ProtectorDependency(const PropertyCellRef & cell)211 explicit ProtectorDependency(const PropertyCellRef& cell) : cell_(cell) {
212 DCHECK_EQ(cell_.value().AsSmi(), Isolate::kProtectorValid);
213 }
214
IsValid() const215 bool IsValid() const override {
216 Handle<PropertyCell> cell = cell_.object<PropertyCell>();
217 return cell->value() == Smi::FromInt(Isolate::kProtectorValid);
218 }
219
Install(MaybeObjectHandle code)220 void Install(MaybeObjectHandle code) override {
221 SLOW_DCHECK(IsValid());
222 DependentCode::InstallDependency(cell_.isolate(), code,
223 cell_.object<PropertyCell>(),
224 DependentCode::kPropertyCellChangedGroup);
225 }
226
227 private:
228 PropertyCellRef cell_;
229 };
230
231 class ElementsKindDependency final
232 : public CompilationDependencies::Dependency {
233 public:
234 // TODO(neis): Once the concurrent compiler frontend is always-on, we no
235 // longer need to explicitly store the elements kind.
ElementsKindDependency(const AllocationSiteRef & site,ElementsKind kind)236 ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind)
237 : site_(site), kind_(kind) {
238 DCHECK(AllocationSite::ShouldTrack(kind_));
239 DCHECK_EQ(kind_, site_.PointsToLiteral()
240 ? site_.boilerplate().value().GetElementsKind()
241 : site_.GetElementsKind());
242 }
243
IsValid() const244 bool IsValid() const override {
245 Handle<AllocationSite> site = site_.object<AllocationSite>();
246 ElementsKind kind = site->PointsToLiteral()
247 ? site->boilerplate()->GetElementsKind()
248 : site->GetElementsKind();
249 return kind_ == kind;
250 }
251
Install(MaybeObjectHandle code)252 void Install(MaybeObjectHandle code) override {
253 SLOW_DCHECK(IsValid());
254 DependentCode::InstallDependency(
255 site_.isolate(), code, site_.object<AllocationSite>(),
256 DependentCode::kAllocationSiteTransitionChangedGroup);
257 }
258
259 private:
260 AllocationSiteRef site_;
261 ElementsKind kind_;
262 };
263
264 class InitialMapInstanceSizePredictionDependency final
265 : public CompilationDependencies::Dependency {
266 public:
InitialMapInstanceSizePredictionDependency(const JSFunctionRef & function,int instance_size)267 InitialMapInstanceSizePredictionDependency(const JSFunctionRef& function,
268 int instance_size)
269 : function_(function), instance_size_(instance_size) {}
270
IsValid() const271 bool IsValid() const override {
272 // The dependency is valid if the prediction is the same as the current
273 // slack tracking result.
274 int instance_size =
275 function_.object<JSFunction>()->ComputeInstanceSizeWithMinSlack(
276 function_.isolate());
277 return instance_size == instance_size_;
278 }
279
Install(MaybeObjectHandle code)280 void Install(MaybeObjectHandle code) override {
281 DCHECK(IsValid());
282 // Finish the slack tracking.
283 function_.object<JSFunction>()->CompleteInobjectSlackTrackingIfActive();
284 }
285
286 private:
287 JSFunctionRef function_;
288 int instance_size_;
289 };
290
DependOnInitialMap(const JSFunctionRef & function)291 MapRef CompilationDependencies::DependOnInitialMap(
292 const JSFunctionRef& function) {
293 MapRef map = function.initial_map();
294 dependencies_.push_front(new (zone_) InitialMapDependency(function, map));
295 return map;
296 }
297
DependOnPrototypeProperty(const JSFunctionRef & function)298 ObjectRef CompilationDependencies::DependOnPrototypeProperty(
299 const JSFunctionRef& function) {
300 ObjectRef prototype = function.prototype();
301 dependencies_.push_front(
302 new (zone_) PrototypePropertyDependency(function, prototype));
303 return prototype;
304 }
305
DependOnStableMap(const MapRef & map)306 void CompilationDependencies::DependOnStableMap(const MapRef& map) {
307 if (map.CanTransition()) {
308 dependencies_.push_front(new (zone_) StableMapDependency(map));
309 } else {
310 DCHECK(map.is_stable());
311 }
312 }
313
DependOnTransition(const MapRef & target_map)314 void CompilationDependencies::DependOnTransition(const MapRef& target_map) {
315 if (target_map.CanBeDeprecated()) {
316 dependencies_.push_front(new (zone_) TransitionDependency(target_map));
317 } else {
318 DCHECK(!target_map.is_deprecated());
319 }
320 }
321
DependOnPretenureMode(const AllocationSiteRef & site)322 PretenureFlag CompilationDependencies::DependOnPretenureMode(
323 const AllocationSiteRef& site) {
324 PretenureFlag mode = site.GetPretenureMode();
325 dependencies_.push_front(new (zone_) PretenureModeDependency(site, mode));
326 return mode;
327 }
328
DependOnFieldType(const MapRef & map,int descriptor)329 void CompilationDependencies::DependOnFieldType(const MapRef& map,
330 int descriptor) {
331 MapRef owner = map.FindFieldOwner(descriptor);
332 ObjectRef type = owner.GetFieldType(descriptor);
333 DCHECK(type.equals(map.GetFieldType(descriptor)));
334 dependencies_.push_front(new (zone_)
335 FieldTypeDependency(owner, descriptor, type));
336 }
337
DependOnGlobalProperty(const PropertyCellRef & cell)338 void CompilationDependencies::DependOnGlobalProperty(
339 const PropertyCellRef& cell) {
340 PropertyCellType type = cell.property_details().cell_type();
341 bool read_only = cell.property_details().IsReadOnly();
342 dependencies_.push_front(new (zone_)
343 GlobalPropertyDependency(cell, type, read_only));
344 }
345
DependOnProtector(const PropertyCellRef & cell)346 void CompilationDependencies::DependOnProtector(const PropertyCellRef& cell) {
347 dependencies_.push_front(new (zone_) ProtectorDependency(cell));
348 }
349
DependOnElementsKind(const AllocationSiteRef & site)350 void CompilationDependencies::DependOnElementsKind(
351 const AllocationSiteRef& site) {
352 // Do nothing if the object doesn't have any useful element transitions left.
353 ElementsKind kind = site.PointsToLiteral()
354 ? site.boilerplate().value().GetElementsKind()
355 : site.GetElementsKind();
356 if (AllocationSite::ShouldTrack(kind)) {
357 dependencies_.push_front(new (zone_) ElementsKindDependency(site, kind));
358 }
359 }
360
AreValid() const361 bool CompilationDependencies::AreValid() const {
362 for (auto dep : dependencies_) {
363 if (!dep->IsValid()) return false;
364 }
365 return true;
366 }
367
Commit(Handle<Code> code)368 bool CompilationDependencies::Commit(Handle<Code> code) {
369 // Check validity of all dependencies first, such that we can avoid installing
370 // anything when there's already an invalid dependency.
371 if (!AreValid()) {
372 dependencies_.clear();
373 return false;
374 }
375
376 for (auto dep : dependencies_) {
377 // Check each dependency's validity again right before installing it,
378 // because a GC can trigger invalidation for some dependency kinds.
379 if (!dep->IsValid()) {
380 dependencies_.clear();
381 return false;
382 }
383 dep->Install(MaybeObjectHandle::Weak(code));
384 }
385 dependencies_.clear();
386 return true;
387 }
388
389 namespace {
DependOnStablePrototypeChain(JSHeapBroker * broker,CompilationDependencies * deps,Handle<Map> map,MaybeHandle<JSReceiver> last_prototype)390 void DependOnStablePrototypeChain(JSHeapBroker* broker,
391 CompilationDependencies* deps,
392 Handle<Map> map,
393 MaybeHandle<JSReceiver> last_prototype) {
394 for (PrototypeIterator i(broker->isolate(), map); !i.IsAtEnd(); i.Advance()) {
395 Handle<JSReceiver> const current =
396 PrototypeIterator::GetCurrent<JSReceiver>(i);
397 deps->DependOnStableMap(
398 MapRef(broker, handle(current->map(), broker->isolate())));
399 Handle<JSReceiver> last;
400 if (last_prototype.ToHandle(&last) && last.is_identical_to(current)) {
401 break;
402 }
403 }
404 }
405 } // namespace
406
DependOnStablePrototypeChains(JSHeapBroker * broker,Handle<Context> native_context,std::vector<Handle<Map>> const & receiver_maps,Handle<JSObject> holder)407 void CompilationDependencies::DependOnStablePrototypeChains(
408 JSHeapBroker* broker, Handle<Context> native_context,
409 std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) {
410 Isolate* isolate = holder->GetIsolate();
411 // Determine actual holder and perform prototype chain checks.
412 for (auto map : receiver_maps) {
413 // Perform the implicit ToObject for primitives here.
414 // Implemented according to ES6 section 7.3.2 GetV (V, P).
415 Handle<JSFunction> constructor;
416 if (Map::GetConstructorFunction(map, native_context)
417 .ToHandle(&constructor)) {
418 map = handle(constructor->initial_map(), isolate);
419 }
420 DependOnStablePrototypeChain(broker, this, map, holder);
421 }
422 }
423
DependOnElementsKinds(const AllocationSiteRef & site)424 void CompilationDependencies::DependOnElementsKinds(
425 const AllocationSiteRef& site) {
426 AllocationSiteRef current = site;
427 while (true) {
428 DependOnElementsKind(current);
429 if (!current.nested_site().IsAllocationSite()) break;
430 current = current.nested_site().AsAllocationSite();
431 }
432 CHECK_EQ(current.nested_site().AsSmi(), 0);
433 }
434
SlackTrackingPrediction(MapRef initial_map,int instance_size)435 SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map,
436 int instance_size)
437 : instance_size_(instance_size),
438 inobject_property_count_(
439 (instance_size >> kPointerSizeLog2) -
440 initial_map.GetInObjectPropertiesStartInWords()) {}
441
442 SlackTrackingPrediction
DependOnInitialMapInstanceSizePrediction(const JSFunctionRef & function)443 CompilationDependencies::DependOnInitialMapInstanceSizePrediction(
444 const JSFunctionRef& function) {
445 MapRef initial_map = DependOnInitialMap(function);
446 int instance_size = function.InitialMapInstanceSizeWithMinSlack();
447 // Currently, we always install the prediction dependency. If this turns out
448 // to be too expensive, we can only install the dependency if slack
449 // tracking is active.
450 dependencies_.push_front(
451 new (zone_)
452 InitialMapInstanceSizePredictionDependency(function, instance_size));
453 DCHECK_LE(instance_size, function.initial_map().instance_size());
454 return SlackTrackingPrediction(initial_map, instance_size);
455 }
456
457 } // namespace compiler
458 } // namespace internal
459 } // namespace v8
460