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