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#ifndef V8_COMPILER_ACCESS_INFO_H_
6#define V8_COMPILER_ACCESS_INFO_H_
7
8#include "src/compiler/heap-refs.h"
9#include "src/compiler/types.h"
10#include "src/zone/zone-containers.h"
11
12namespace v8 {
13namespace internal {
14
15// Forward declarations.
16class Factory;
17
18namespace compiler {
19
20// Forward declarations.
21class CompilationDependencies;
22class CompilationDependency;
23class ElementAccessFeedback;
24class JSHeapBroker;
25class TypeCache;
26struct ConstFieldInfo;
27
28std::ostream& operator<<(std::ostream&, AccessMode);
29
30// This class encapsulates all information required to access a certain element.
31class ElementAccessInfo final {
32 public:
33  ElementAccessInfo(ZoneVector<MapRef>&& lookup_start_object_maps,
34                    ElementsKind elements_kind, Zone* zone);
35
36  ElementsKind elements_kind() const { return elements_kind_; }
37  ZoneVector<MapRef> const& lookup_start_object_maps() const {
38    return lookup_start_object_maps_;
39  }
40  ZoneVector<MapRef> const& transition_sources() const {
41    return transition_sources_;
42  }
43
44  void AddTransitionSource(MapRef map) {
45    CHECK_EQ(lookup_start_object_maps_.size(), 1);
46    transition_sources_.push_back(map);
47  }
48
49 private:
50  ElementsKind elements_kind_;
51  ZoneVector<MapRef> lookup_start_object_maps_;
52  ZoneVector<MapRef> transition_sources_;
53};
54
55// This class encapsulates all information required to access a certain
56// object property, either on the object itself or on the prototype chain.
57class PropertyAccessInfo final {
58 public:
59  enum Kind {
60    kInvalid,
61    kNotFound,
62    kDataField,
63    kFastDataConstant,
64    kDictionaryProtoDataConstant,
65    kFastAccessorConstant,
66    kDictionaryProtoAccessorConstant,
67    kModuleExport,
68    kStringLength
69  };
70
71  static PropertyAccessInfo NotFound(Zone* zone, MapRef receiver_map,
72                                     base::Optional<JSObjectRef> holder);
73  static PropertyAccessInfo DataField(
74      Zone* zone, MapRef receiver_map,
75      ZoneVector<CompilationDependency const*>&& unrecorded_dependencies,
76      FieldIndex field_index, Representation field_representation,
77      Type field_type, MapRef field_owner_map, base::Optional<MapRef> field_map,
78      base::Optional<JSObjectRef> holder,
79      base::Optional<MapRef> transition_map);
80  static PropertyAccessInfo FastDataConstant(
81      Zone* zone, MapRef receiver_map,
82      ZoneVector<CompilationDependency const*>&& unrecorded_dependencies,
83      FieldIndex field_index, Representation field_representation,
84      Type field_type, MapRef field_owner_map, base::Optional<MapRef> field_map,
85      base::Optional<JSObjectRef> holder,
86      base::Optional<MapRef> transition_map);
87  static PropertyAccessInfo FastAccessorConstant(
88      Zone* zone, MapRef receiver_map, base::Optional<ObjectRef> constant,
89      base::Optional<JSObjectRef> holder);
90  static PropertyAccessInfo ModuleExport(Zone* zone, MapRef receiver_map,
91                                         CellRef cell);
92  static PropertyAccessInfo StringLength(Zone* zone, MapRef receiver_map);
93  static PropertyAccessInfo Invalid(Zone* zone);
94  static PropertyAccessInfo DictionaryProtoDataConstant(
95      Zone* zone, MapRef receiver_map, JSObjectRef holder,
96      InternalIndex dict_index, NameRef name);
97  static PropertyAccessInfo DictionaryProtoAccessorConstant(
98      Zone* zone, MapRef receiver_map, base::Optional<JSObjectRef> holder,
99      ObjectRef constant, NameRef name);
100
101  bool Merge(PropertyAccessInfo const* that, AccessMode access_mode,
102             Zone* zone) V8_WARN_UNUSED_RESULT;
103
104  void RecordDependencies(CompilationDependencies* dependencies);
105
106  bool IsInvalid() const { return kind() == kInvalid; }
107  bool IsNotFound() const { return kind() == kNotFound; }
108  bool IsDataField() const { return kind() == kDataField; }
109  bool IsFastDataConstant() const { return kind() == kFastDataConstant; }
110  bool IsFastAccessorConstant() const {
111    return kind() == kFastAccessorConstant;
112  }
113  bool IsModuleExport() const { return kind() == kModuleExport; }
114  bool IsStringLength() const { return kind() == kStringLength; }
115  bool IsDictionaryProtoDataConstant() const {
116    return kind() == kDictionaryProtoDataConstant;
117  }
118  bool IsDictionaryProtoAccessorConstant() const {
119    return kind() == kDictionaryProtoAccessorConstant;
120  }
121
122  bool HasTransitionMap() const { return transition_map().has_value(); }
123  bool HasDictionaryHolder() const {
124    return kind_ == kDictionaryProtoDataConstant ||
125           kind_ == kDictionaryProtoAccessorConstant;
126  }
127  ConstFieldInfo GetConstFieldInfo() const;
128
129  Kind kind() const { return kind_; }
130  base::Optional<JSObjectRef> holder() const {
131    // TODO(neis): There was a CHECK here that tries to protect against
132    // using the access info without recording its dependencies first.
133    // Find a more suitable place for it.
134    return holder_;
135  }
136  base::Optional<MapRef> transition_map() const {
137    DCHECK(!HasDictionaryHolder());
138    return transition_map_;
139  }
140  base::Optional<ObjectRef> constant() const {
141    DCHECK_IMPLIES(constant_.has_value(),
142                   IsModuleExport() || IsFastAccessorConstant() ||
143                       IsDictionaryProtoAccessorConstant());
144    return constant_;
145  }
146  FieldIndex field_index() const {
147    DCHECK(!HasDictionaryHolder());
148    return field_index_;
149  }
150
151  Type field_type() const {
152    DCHECK(!HasDictionaryHolder());
153    return field_type_;
154  }
155  Representation field_representation() const {
156    DCHECK(!HasDictionaryHolder());
157    return field_representation_;
158  }
159  base::Optional<MapRef> field_map() const {
160    DCHECK(!HasDictionaryHolder());
161    return field_map_;
162  }
163  ZoneVector<MapRef> const& lookup_start_object_maps() const {
164    return lookup_start_object_maps_;
165  }
166
167  InternalIndex dictionary_index() const {
168    DCHECK(HasDictionaryHolder());
169    return dictionary_index_;
170  }
171
172  NameRef name() const {
173    DCHECK(HasDictionaryHolder());
174    return name_.value();
175  }
176
177 private:
178  explicit PropertyAccessInfo(Zone* zone);
179  PropertyAccessInfo(Zone* zone, Kind kind, base::Optional<JSObjectRef> holder,
180                     ZoneVector<MapRef>&& lookup_start_object_maps);
181  PropertyAccessInfo(Zone* zone, Kind kind, base::Optional<JSObjectRef> holder,
182                     base::Optional<ObjectRef> constant,
183                     base::Optional<NameRef> name,
184                     ZoneVector<MapRef>&& lookup_start_object_maps);
185  PropertyAccessInfo(Kind kind, base::Optional<JSObjectRef> holder,
186                     base::Optional<MapRef> transition_map,
187                     FieldIndex field_index,
188                     Representation field_representation, Type field_type,
189                     MapRef field_owner_map, base::Optional<MapRef> field_map,
190                     ZoneVector<MapRef>&& lookup_start_object_maps,
191                     ZoneVector<CompilationDependency const*>&& dependencies);
192  PropertyAccessInfo(Zone* zone, Kind kind, base::Optional<JSObjectRef> holder,
193                     ZoneVector<MapRef>&& lookup_start_object_maps,
194                     InternalIndex dictionary_index, NameRef name);
195
196  // Members used for fast and dictionary mode holders:
197  Kind kind_;
198  ZoneVector<MapRef> lookup_start_object_maps_;
199  base::Optional<ObjectRef> constant_;
200  base::Optional<JSObjectRef> holder_;
201
202  // Members only used for fast mode holders:
203  ZoneVector<CompilationDependency const*> unrecorded_dependencies_;
204  base::Optional<MapRef> transition_map_;
205  FieldIndex field_index_;
206  Representation field_representation_;
207  Type field_type_;
208  base::Optional<MapRef> field_owner_map_;
209  base::Optional<MapRef> field_map_;
210
211  // Members only used for dictionary mode holders:
212  InternalIndex dictionary_index_;
213  base::Optional<NameRef> name_;
214};
215
216// Factory class for {ElementAccessInfo}s and {PropertyAccessInfo}s.
217class AccessInfoFactory final {
218 public:
219  AccessInfoFactory(JSHeapBroker* broker, CompilationDependencies* dependencies,
220                    Zone* zone);
221
222  base::Optional<ElementAccessInfo> ComputeElementAccessInfo(
223      MapRef map, AccessMode access_mode) const;
224  bool ComputeElementAccessInfos(
225      ElementAccessFeedback const& feedback,
226      ZoneVector<ElementAccessInfo>* access_infos) const;
227
228  PropertyAccessInfo ComputePropertyAccessInfo(MapRef map, NameRef name,
229                                               AccessMode access_mode) const;
230
231  PropertyAccessInfo ComputeDictionaryProtoAccessInfo(
232      MapRef receiver_map, NameRef name, JSObjectRef holder,
233      InternalIndex dict_index, AccessMode access_mode,
234      PropertyDetails details) const;
235
236  // Merge as many of the given {infos} as possible and record any dependencies.
237  // Return false iff any of them was invalid, in which case no dependencies are
238  // recorded.
239  // TODO(neis): Make access_mode part of access info?
240  bool FinalizePropertyAccessInfos(
241      ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
242      ZoneVector<PropertyAccessInfo>* result) const;
243
244  // Merge the given {infos} to a single one and record any dependencies. If the
245  // merge is not possible, the result has kind {kInvalid} and no dependencies
246  // are recorded.
247  PropertyAccessInfo FinalizePropertyAccessInfosAsOne(
248      ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode) const;
249
250 private:
251  base::Optional<ElementAccessInfo> ConsolidateElementLoad(
252      ElementAccessFeedback const& feedback) const;
253  PropertyAccessInfo LookupSpecialFieldAccessor(MapRef map, NameRef name) const;
254  PropertyAccessInfo LookupTransition(MapRef map, NameRef name,
255                                      base::Optional<JSObjectRef> holder,
256                                      PropertyAttributes attrs) const;
257  PropertyAccessInfo ComputeDataFieldAccessInfo(
258      MapRef receiver_map, MapRef map, NameRef name,
259      base::Optional<JSObjectRef> holder, InternalIndex descriptor,
260      AccessMode access_mode) const;
261  PropertyAccessInfo ComputeAccessorDescriptorAccessInfo(
262      MapRef receiver_map, NameRef name, MapRef map,
263      base::Optional<JSObjectRef> holder, InternalIndex descriptor,
264      AccessMode access_mode) const;
265
266  PropertyAccessInfo Invalid() const {
267    return PropertyAccessInfo::Invalid(zone());
268  }
269
270  void MergePropertyAccessInfos(ZoneVector<PropertyAccessInfo> infos,
271                                AccessMode access_mode,
272                                ZoneVector<PropertyAccessInfo>* result) const;
273
274  bool TryLoadPropertyDetails(MapRef map,
275                              base::Optional<JSObjectRef> maybe_holder,
276                              NameRef name, InternalIndex* index_out,
277                              PropertyDetails* details_out) const;
278
279  CompilationDependencies* dependencies() const { return dependencies_; }
280  JSHeapBroker* broker() const { return broker_; }
281  Isolate* isolate() const;
282  Zone* zone() const { return zone_; }
283
284  JSHeapBroker* const broker_;
285  CompilationDependencies* const dependencies_;
286  TypeCache const* const type_cache_;
287  Zone* const zone_;
288
289  AccessInfoFactory(const AccessInfoFactory&) = delete;
290  AccessInfoFactory& operator=(const AccessInfoFactory&) = delete;
291};
292
293}  // namespace compiler
294}  // namespace internal
295}  // namespace v8
296
297#endif  // V8_COMPILER_ACCESS_INFO_H_
298