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/lookup.h"
14 
15 #include "src/field-index-inl.h"
16 #include "src/isolate-inl.h"
17 
18 namespace v8 {
19 namespace internal {
20 namespace compiler {
21 
graph() const22 Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); }
23 
isolate() const24 Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); }
25 
common() const26 CommonOperatorBuilder* PropertyAccessBuilder::common() const {
27   return jsgraph()->common();
28 }
29 
simplified() const30 SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const {
31   return jsgraph()->simplified();
32 }
33 
HasOnlyStringMaps(MapHandles const & maps)34 bool HasOnlyStringMaps(MapHandles const& maps) {
35   for (auto map : maps) {
36     if (!map->IsStringMap()) return false;
37   }
38   return true;
39 }
40 
41 namespace {
42 
HasOnlyNumberMaps(MapHandles const & maps)43 bool HasOnlyNumberMaps(MapHandles const& maps) {
44   for (auto map : maps) {
45     if (map->instance_type() != HEAP_NUMBER_TYPE) return false;
46   }
47   return true;
48 }
49 
50 }  // namespace
51 
TryBuildStringCheck(MapHandles const & maps,Node ** receiver,Node ** effect,Node * control)52 bool PropertyAccessBuilder::TryBuildStringCheck(MapHandles const& maps,
53                                                 Node** receiver, Node** effect,
54                                                 Node* control) {
55   if (HasOnlyStringMaps(maps)) {
56     // Monormorphic string access (ignoring the fact that there are multiple
57     // String maps).
58     *receiver = *effect =
59         graph()->NewNode(simplified()->CheckString(VectorSlotPair()), *receiver,
60                          *effect, control);
61     return true;
62   }
63   return false;
64 }
65 
TryBuildNumberCheck(MapHandles const & maps,Node ** receiver,Node ** effect,Node * control)66 bool PropertyAccessBuilder::TryBuildNumberCheck(MapHandles const& maps,
67                                                 Node** receiver, Node** effect,
68                                                 Node* control) {
69   if (HasOnlyNumberMaps(maps)) {
70     // Monomorphic number access (we also deal with Smis here).
71     *receiver = *effect =
72         graph()->NewNode(simplified()->CheckNumber(VectorSlotPair()), *receiver,
73                          *effect, control);
74     return true;
75   }
76   return false;
77 }
78 
79 namespace {
80 
NeedsCheckHeapObject(Node * receiver)81 bool NeedsCheckHeapObject(Node* receiver) {
82   switch (receiver->opcode()) {
83     case IrOpcode::kConvertReceiver:
84     case IrOpcode::kHeapConstant:
85     case IrOpcode::kJSCreate:
86     case IrOpcode::kJSCreateArguments:
87     case IrOpcode::kJSCreateArray:
88     case IrOpcode::kJSCreateClosure:
89     case IrOpcode::kJSCreateIterResultObject:
90     case IrOpcode::kJSCreateLiteralArray:
91     case IrOpcode::kJSCreateEmptyLiteralArray:
92     case IrOpcode::kJSCreateLiteralObject:
93     case IrOpcode::kJSCreateEmptyLiteralObject:
94     case IrOpcode::kJSCreateLiteralRegExp:
95     case IrOpcode::kJSCreateGeneratorObject:
96     case IrOpcode::kJSConstructForwardVarargs:
97     case IrOpcode::kJSConstruct:
98     case IrOpcode::kJSConstructWithArrayLike:
99     case IrOpcode::kJSConstructWithSpread:
100     case IrOpcode::kJSToName:
101     case IrOpcode::kJSToString:
102     case IrOpcode::kJSToObject:
103     case IrOpcode::kTypeOf:
104     case IrOpcode::kJSGetSuperConstructor:
105       return false;
106     case IrOpcode::kPhi: {
107       Node* control = NodeProperties::GetControlInput(receiver);
108       if (control->opcode() != IrOpcode::kMerge) return true;
109       for (int i = 0; i < receiver->InputCount() - 1; ++i) {
110         if (NeedsCheckHeapObject(receiver->InputAt(i))) return true;
111       }
112       return false;
113     }
114     default:
115       return true;
116   }
117 }
118 
119 }  // namespace
120 
BuildCheckHeapObject(Node * receiver,Node ** effect,Node * control)121 Node* PropertyAccessBuilder::BuildCheckHeapObject(Node* receiver, Node** effect,
122                                                   Node* control) {
123   if (NeedsCheckHeapObject(receiver)) {
124     receiver = *effect = graph()->NewNode(simplified()->CheckHeapObject(),
125                                           receiver, *effect, control);
126   }
127   return receiver;
128 }
129 
BuildCheckMaps(Node * receiver,Node ** effect,Node * control,std::vector<Handle<Map>> const & receiver_maps)130 void PropertyAccessBuilder::BuildCheckMaps(
131     Node* receiver, Node** effect, Node* control,
132     std::vector<Handle<Map>> const& receiver_maps) {
133   HeapObjectMatcher m(receiver);
134   if (m.HasValue()) {
135     Handle<Map> receiver_map(m.Value()->map(), isolate());
136     if (receiver_map->is_stable()) {
137       for (Handle<Map> map : receiver_maps) {
138         if (map.is_identical_to(receiver_map)) {
139           dependencies()->DependOnStableMap(
140               MapRef(js_heap_broker(), receiver_map));
141           return;
142         }
143       }
144     }
145   }
146   ZoneHandleSet<Map> maps;
147   CheckMapsFlags flags = CheckMapsFlag::kNone;
148   for (Handle<Map> map : receiver_maps) {
149     maps.insert(map, graph()->zone());
150     if (map->is_migration_target()) {
151       flags |= CheckMapsFlag::kTryMigrateInstance;
152     }
153   }
154   *effect = graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
155                              *effect, control);
156 }
157 
BuildCheckValue(Node * receiver,Node ** effect,Node * control,Handle<HeapObject> value)158 Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Node** effect,
159                                              Node* control,
160                                              Handle<HeapObject> value) {
161   HeapObjectMatcher m(receiver);
162   if (m.Is(value)) return receiver;
163   Node* expected = jsgraph()->HeapConstant(value);
164   Node* check =
165       graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
166   *effect =
167       graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
168                        check, *effect, control);
169   return expected;
170 }
171 
ResolveHolder(PropertyAccessInfo const & access_info,Node * receiver)172 Node* PropertyAccessBuilder::ResolveHolder(
173     PropertyAccessInfo const& access_info, Node* receiver) {
174   Handle<JSObject> holder;
175   if (access_info.holder().ToHandle(&holder)) {
176     return jsgraph()->Constant(holder);
177   }
178   return receiver;
179 }
180 
TryBuildLoadConstantDataField(Handle<Name> name,PropertyAccessInfo const & access_info,Node * receiver)181 Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
182     Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver) {
183   // Optimize immutable property loads.
184   HeapObjectMatcher m(receiver);
185   if (m.HasValue() && m.Value()->IsJSObject()) {
186     // TODO(ishell): Use something simpler like
187     //
188     // Handle<Object> value =
189     //     JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()),
190     //                              Representation::Tagged(), field_index);
191     //
192     // here, once we have the immutable bit in the access_info.
193 
194     // TODO(turbofan): Given that we already have the field_index here, we
195     // might be smarter in the future and not rely on the LookupIterator.
196     LookupIterator it(isolate(), m.Value(), name,
197                       LookupIterator::OWN_SKIP_INTERCEPTOR);
198     if (it.state() == LookupIterator::DATA) {
199       bool is_readonly_non_configurable =
200           it.IsReadOnly() && !it.IsConfigurable();
201       if (is_readonly_non_configurable ||
202           (FLAG_track_constant_fields && access_info.IsDataConstantField())) {
203         Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
204         if (!is_readonly_non_configurable) {
205           // It's necessary to add dependency on the map that introduced
206           // the field.
207           DCHECK(access_info.IsDataConstantField());
208           DCHECK(!it.is_dictionary_holder());
209           MapRef map(js_heap_broker(),
210                      handle(it.GetHolder<HeapObject>()->map(), isolate()));
211           dependencies()->DependOnFieldType(map, it.GetFieldDescriptorIndex());
212         }
213         return value;
214       }
215     }
216   }
217   return nullptr;
218 }
219 
BuildLoadDataField(Handle<Name> name,PropertyAccessInfo const & access_info,Node * receiver,Node ** effect,Node ** control)220 Node* PropertyAccessBuilder::BuildLoadDataField(
221     Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver,
222     Node** effect, Node** control) {
223   DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
224   receiver = ResolveHolder(access_info, receiver);
225   if (Node* value =
226           TryBuildLoadConstantDataField(name, access_info, receiver)) {
227     return value;
228   }
229 
230   FieldIndex const field_index = access_info.field_index();
231   Type const field_type = access_info.field_type();
232   MachineRepresentation const field_representation =
233       access_info.field_representation();
234   Node* storage = receiver;
235   if (!field_index.is_inobject()) {
236     storage = *effect = graph()->NewNode(
237         simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
238         storage, *effect, *control);
239   }
240   FieldAccess field_access = {
241       kTaggedBase,
242       field_index.offset(),
243       name,
244       MaybeHandle<Map>(),
245       field_type,
246       MachineType::TypeForRepresentation(field_representation),
247       kFullWriteBarrier};
248   if (field_representation == MachineRepresentation::kFloat64) {
249     if (!field_index.is_inobject() || field_index.is_hidden_field() ||
250         !FLAG_unbox_double_fields) {
251       FieldAccess const storage_access = {kTaggedBase,
252                                           field_index.offset(),
253                                           name,
254                                           MaybeHandle<Map>(),
255                                           Type::OtherInternal(),
256                                           MachineType::TaggedPointer(),
257                                           kPointerWriteBarrier};
258       storage = *effect = graph()->NewNode(
259           simplified()->LoadField(storage_access), storage, *effect, *control);
260       field_access.offset = HeapNumber::kValueOffset;
261       field_access.name = MaybeHandle<Name>();
262     }
263   } else if (field_representation == MachineRepresentation::kTaggedPointer) {
264     // Remember the map of the field value, if its map is stable. This is
265     // used by the LoadElimination to eliminate map checks on the result.
266     Handle<Map> field_map;
267     if (access_info.field_map().ToHandle(&field_map)) {
268       if (field_map->is_stable()) {
269         dependencies()->DependOnStableMap(MapRef(js_heap_broker(), field_map));
270         field_access.map = field_map;
271       }
272     }
273   }
274   Node* value = *effect = graph()->NewNode(
275       simplified()->LoadField(field_access), storage, *effect, *control);
276   return value;
277 }
278 
279 }  // namespace compiler
280 }  // namespace internal
281 }  // namespace v8
282