• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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