• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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/property-access-builder.h"
6 
7 #include "src/compiler/access-builder.h"
8 #include "src/compiler/access-info.h"
9 #include "src/compiler/compilation-dependencies.h"
10 #include "src/compiler/js-graph.h"
11 #include "src/compiler/node-matchers.h"
12 #include "src/compiler/simplified-operator.h"
13 #include "src/objects/heap-number.h"
14 #include "src/objects/lookup.h"
15 
16 #include "src/execution/isolate-inl.h"
17 #include "src/objects/field-index-inl.h"
18 
19 namespace v8 {
20 namespace internal {
21 namespace compiler {
22 
graph() const23 Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); }
24 
isolate() const25 Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); }
26 
common() const27 CommonOperatorBuilder* PropertyAccessBuilder::common() const {
28   return jsgraph()->common();
29 }
30 
simplified() const31 SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const {
32   return jsgraph()->simplified();
33 }
34 
HasOnlyStringMaps(JSHeapBroker * broker,ZoneVector<Handle<Map>> const & maps)35 bool HasOnlyStringMaps(JSHeapBroker* broker,
36                        ZoneVector<Handle<Map>> const& maps) {
37   for (auto map : maps) {
38     MapRef map_ref(broker, map);
39     if (!map_ref.IsStringMap()) return false;
40   }
41   return true;
42 }
43 
44 namespace {
45 
HasOnlyNumberMaps(JSHeapBroker * broker,ZoneVector<Handle<Map>> const & maps)46 bool HasOnlyNumberMaps(JSHeapBroker* broker,
47                        ZoneVector<Handle<Map>> const& maps) {
48   for (auto map : maps) {
49     MapRef map_ref(broker, map);
50     if (map_ref.instance_type() != HEAP_NUMBER_TYPE) return false;
51   }
52   return true;
53 }
54 
55 }  // namespace
56 
TryBuildStringCheck(JSHeapBroker * broker,ZoneVector<Handle<Map>> const & maps,Node ** receiver,Node ** effect,Node * control)57 bool PropertyAccessBuilder::TryBuildStringCheck(
58     JSHeapBroker* broker, ZoneVector<Handle<Map>> const& maps, Node** receiver,
59     Node** effect, Node* control) {
60   if (HasOnlyStringMaps(broker, maps)) {
61     // Monormorphic string access (ignoring the fact that there are multiple
62     // String maps).
63     *receiver = *effect =
64         graph()->NewNode(simplified()->CheckString(FeedbackSource()), *receiver,
65                          *effect, control);
66     return true;
67   }
68   return false;
69 }
70 
TryBuildNumberCheck(JSHeapBroker * broker,ZoneVector<Handle<Map>> const & maps,Node ** receiver,Node ** effect,Node * control)71 bool PropertyAccessBuilder::TryBuildNumberCheck(
72     JSHeapBroker* broker, ZoneVector<Handle<Map>> const& maps, Node** receiver,
73     Node** effect, Node* control) {
74   if (HasOnlyNumberMaps(broker, maps)) {
75     // Monomorphic number access (we also deal with Smis here).
76     *receiver = *effect =
77         graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), *receiver,
78                          *effect, control);
79     return true;
80   }
81   return false;
82 }
83 
BuildCheckMaps(Node * object,Node ** effect,Node * control,ZoneVector<Handle<Map>> const & maps)84 void PropertyAccessBuilder::BuildCheckMaps(
85     Node* object, Node** effect, Node* control,
86     ZoneVector<Handle<Map>> const& maps) {
87   HeapObjectMatcher m(object);
88   if (m.HasResolvedValue()) {
89     MapRef object_map = m.Ref(broker()).map();
90     if (object_map.is_stable()) {
91       for (Handle<Map> map : maps) {
92         if (MapRef(broker(), map).equals(object_map)) {
93           dependencies()->DependOnStableMap(object_map);
94           return;
95         }
96       }
97     }
98   }
99   ZoneHandleSet<Map> map_set;
100   CheckMapsFlags flags = CheckMapsFlag::kNone;
101   for (Handle<Map> map : maps) {
102     MapRef object_map(broker(), map);
103     map_set.insert(object_map.object(), graph()->zone());
104     if (object_map.is_migration_target()) {
105       flags |= CheckMapsFlag::kTryMigrateInstance;
106     }
107   }
108   *effect = graph()->NewNode(simplified()->CheckMaps(flags, map_set), object,
109                              *effect, control);
110 }
111 
BuildCheckValue(Node * receiver,Effect * effect,Control control,Handle<HeapObject> value)112 Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Effect* effect,
113                                              Control control,
114                                              Handle<HeapObject> value) {
115   HeapObjectMatcher m(receiver);
116   if (m.Is(value)) return receiver;
117   Node* expected = jsgraph()->HeapConstant(value);
118   Node* check =
119       graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
120   *effect =
121       graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
122                        check, *effect, control);
123   return expected;
124 }
125 
ResolveHolder(PropertyAccessInfo const & access_info,Node * lookup_start_object)126 Node* PropertyAccessBuilder::ResolveHolder(
127     PropertyAccessInfo const& access_info, Node* lookup_start_object) {
128   Handle<JSObject> holder;
129   if (access_info.holder().ToHandle(&holder)) {
130     return jsgraph()->Constant(ObjectRef(broker(), holder));
131   }
132   return lookup_start_object;
133 }
134 
ConvertRepresentation(Representation representation)135 MachineRepresentation PropertyAccessBuilder::ConvertRepresentation(
136     Representation representation) {
137   switch (representation.kind()) {
138     case Representation::kSmi:
139       return MachineRepresentation::kTaggedSigned;
140     case Representation::kDouble:
141       return MachineRepresentation::kFloat64;
142     case Representation::kHeapObject:
143       return MachineRepresentation::kTaggedPointer;
144     case Representation::kTagged:
145       return MachineRepresentation::kTagged;
146     default:
147       UNREACHABLE();
148   }
149 }
150 
TryBuildLoadConstantDataField(NameRef const & name,PropertyAccessInfo const & access_info,Node * lookup_start_object)151 Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
152     NameRef const& name, PropertyAccessInfo const& access_info,
153     Node* lookup_start_object) {
154   if (!access_info.IsDataConstant()) return nullptr;
155 
156   // First, determine if we have a constant holder to load from.
157   Handle<JSObject> holder;
158   // If {access_info} has a holder, just use it.
159   if (!access_info.holder().ToHandle(&holder)) {
160     // Otherwise, try to match the {lookup_start_object} as a constant.
161     HeapObjectMatcher m(lookup_start_object);
162     if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSObject()) return nullptr;
163 
164     // Let us make sure the actual map of the constant lookup_start_object is
165     // among the maps in {access_info}.
166     MapRef lookup_start_object_map = m.Ref(broker()).map();
167     if (std::find_if(
168             access_info.lookup_start_object_maps().begin(),
169             access_info.lookup_start_object_maps().end(), [&](Handle<Map> map) {
170               return MapRef(broker(), map).equals(lookup_start_object_map);
171             }) == access_info.lookup_start_object_maps().end()) {
172       // The map of the lookup_start_object is not in the feedback, let us bail
173       // out.
174       return nullptr;
175     }
176     holder = m.Ref(broker()).AsJSObject().object();
177   }
178 
179   JSObjectRef holder_ref(broker(), holder);
180   base::Optional<ObjectRef> value = holder_ref.GetOwnDataProperty(
181       access_info.field_representation(), access_info.field_index());
182   if (!value.has_value()) {
183     return nullptr;
184   }
185   return jsgraph()->Constant(*value);
186 }
187 
BuildLoadDataField(NameRef const & name,Node * holder,FieldAccess & field_access,bool is_inobject,Node ** effect,Node ** control)188 Node* PropertyAccessBuilder::BuildLoadDataField(NameRef const& name,
189                                                 Node* holder,
190                                                 FieldAccess& field_access,
191                                                 bool is_inobject, Node** effect,
192                                                 Node** control) {
193   Node* storage = holder;
194   if (!is_inobject) {
195     storage = *effect = graph()->NewNode(
196         simplified()->LoadField(
197             AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()),
198         storage, *effect, *control);
199   }
200   if (field_access.machine_type.representation() ==
201       MachineRepresentation::kFloat64) {
202     bool const is_heapnumber = !is_inobject || !FLAG_unbox_double_fields;
203     if (is_heapnumber) {
204       if (dependencies() == nullptr) {
205         FieldAccess const storage_access = {kTaggedBase,
206                                             field_access.offset,
207                                             name.object(),
208                                             MaybeHandle<Map>(),
209                                             Type::Any(),
210                                             MachineType::AnyTagged(),
211                                             kPointerWriteBarrier,
212                                             LoadSensitivity::kCritical,
213                                             field_access.const_field_info};
214         storage = *effect =
215             graph()->NewNode(simplified()->LoadField(storage_access), storage,
216                              *effect, *control);
217         // We expect the loaded value to be a heap number here. With
218         // in-place field representation changes it is possible this is a
219         // no longer a heap number without map transitions. If we haven't taken
220         // a dependency on field representation, we should verify the loaded
221         // value is a heap number.
222         storage = *effect = graph()->NewNode(simplified()->CheckHeapObject(),
223                                              storage, *effect, *control);
224         Node* map = *effect =
225             graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
226                              storage, *effect, *control);
227         Node* is_heap_number =
228             graph()->NewNode(simplified()->ReferenceEqual(), map,
229                              jsgraph()->HeapNumberMapConstant());
230         *effect = graph()->NewNode(
231             simplified()->CheckIf(DeoptimizeReason::kNotAHeapNumber),
232             is_heap_number, *effect, *control);
233       } else {
234         FieldAccess const storage_access = {kTaggedBase,
235                                             field_access.offset,
236                                             name.object(),
237                                             MaybeHandle<Map>(),
238                                             Type::OtherInternal(),
239                                             MachineType::TaggedPointer(),
240                                             kPointerWriteBarrier,
241                                             LoadSensitivity::kCritical,
242                                             field_access.const_field_info};
243         storage = *effect =
244             graph()->NewNode(simplified()->LoadField(storage_access), storage,
245                              *effect, *control);
246       }
247       field_access.offset = HeapNumber::kValueOffset;
248       field_access.name = MaybeHandle<Name>();
249     }
250   }
251   Node* value = *effect = graph()->NewNode(
252       simplified()->LoadField(field_access), storage, *effect, *control);
253   return value;
254 }
255 
BuildMinimorphicLoadDataField(NameRef const & name,MinimorphicLoadPropertyAccessInfo const & access_info,Node * lookup_start_object,Node ** effect,Node ** control)256 Node* PropertyAccessBuilder::BuildMinimorphicLoadDataField(
257     NameRef const& name, MinimorphicLoadPropertyAccessInfo const& access_info,
258     Node* lookup_start_object, Node** effect, Node** control) {
259   DCHECK_NULL(dependencies());
260   MachineRepresentation const field_representation =
261       ConvertRepresentation(access_info.field_representation());
262 
263   FieldAccess field_access = {
264       kTaggedBase,
265       access_info.offset(),
266       name.object(),
267       MaybeHandle<Map>(),
268       access_info.field_type(),
269       MachineType::TypeForRepresentation(field_representation),
270       kFullWriteBarrier,
271       LoadSensitivity::kCritical,
272       ConstFieldInfo::None()};
273   return BuildLoadDataField(name, lookup_start_object, field_access,
274                             access_info.is_inobject(), effect, control);
275 }
276 
BuildLoadDataField(NameRef const & name,PropertyAccessInfo const & access_info,Node * lookup_start_object,Node ** effect,Node ** control)277 Node* PropertyAccessBuilder::BuildLoadDataField(
278     NameRef const& name, PropertyAccessInfo const& access_info,
279     Node* lookup_start_object, Node** effect, Node** control) {
280   DCHECK(access_info.IsDataField() || access_info.IsDataConstant());
281   if (Node* value = TryBuildLoadConstantDataField(name, access_info,
282                                                   lookup_start_object)) {
283     return value;
284   }
285 
286   MachineRepresentation const field_representation =
287       ConvertRepresentation(access_info.field_representation());
288   Node* storage = ResolveHolder(access_info, lookup_start_object);
289 
290   FieldAccess field_access = {
291       kTaggedBase,
292       access_info.field_index().offset(),
293       name.object(),
294       MaybeHandle<Map>(),
295       access_info.field_type(),
296       MachineType::TypeForRepresentation(field_representation),
297       kFullWriteBarrier,
298       LoadSensitivity::kCritical,
299       access_info.GetConstFieldInfo()};
300   if (field_representation == MachineRepresentation::kTaggedPointer ||
301       field_representation == MachineRepresentation::kCompressedPointer) {
302     // Remember the map of the field value, if its map is stable. This is
303     // used by the LoadElimination to eliminate map checks on the result.
304     Handle<Map> field_map;
305     if (access_info.field_map().ToHandle(&field_map)) {
306       MapRef field_map_ref(broker(), field_map);
307       if (field_map_ref.is_stable()) {
308         dependencies()->DependOnStableMap(field_map_ref);
309         field_access.map = field_map;
310       }
311     }
312   }
313   return BuildLoadDataField(name, storage, field_access,
314                             access_info.field_index().is_inobject(), effect,
315                             control);
316 }
317 
318 }  // namespace compiler
319 }  // namespace internal
320 }  // namespace v8
321