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/compilation-dependencies.h"
8 #include "src/compiler/access-builder.h"
9 #include "src/compiler/access-info.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()->AssumeMapStable(receiver_map);
140           return;
141         }
142       }
143     }
144   }
145   ZoneHandleSet<Map> maps;
146   CheckMapsFlags flags = CheckMapsFlag::kNone;
147   for (Handle<Map> map : receiver_maps) {
148     maps.insert(map, graph()->zone());
149     if (map->is_migration_target()) {
150       flags |= CheckMapsFlag::kTryMigrateInstance;
151     }
152   }
153   *effect = graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
154                              *effect, control);
155 }
156 
BuildCheckValue(Node * receiver,Node ** effect,Node * control,Handle<HeapObject> value)157 Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Node** effect,
158                                              Node* control,
159                                              Handle<HeapObject> value) {
160   HeapObjectMatcher m(receiver);
161   if (m.Is(value)) return receiver;
162   Node* expected = jsgraph()->HeapConstant(value);
163   Node* check =
164       graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
165   *effect =
166       graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
167                        check, *effect, control);
168   return expected;
169 }
170 
AssumePrototypesStable(Handle<Context> native_context,std::vector<Handle<Map>> const & receiver_maps,Handle<JSObject> holder)171 void PropertyAccessBuilder::AssumePrototypesStable(
172     Handle<Context> native_context,
173     std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) {
174   // Determine actual holder and perform prototype chain checks.
175   for (auto map : receiver_maps) {
176     // Perform the implicit ToObject for primitives here.
177     // Implemented according to ES6 section 7.3.2 GetV (V, P).
178     Handle<JSFunction> constructor;
179     if (Map::GetConstructorFunction(map, native_context)
180             .ToHandle(&constructor)) {
181       map = handle(constructor->initial_map(), holder->GetIsolate());
182     }
183     dependencies()->AssumePrototypeMapsStable(map, holder);
184   }
185 }
186 
ResolveHolder(PropertyAccessInfo const & access_info,Node * receiver)187 Node* PropertyAccessBuilder::ResolveHolder(
188     PropertyAccessInfo const& access_info, Node* receiver) {
189   Handle<JSObject> holder;
190   if (access_info.holder().ToHandle(&holder)) {
191     return jsgraph()->Constant(holder);
192   }
193   return receiver;
194 }
195 
TryBuildLoadConstantDataField(Handle<Name> name,PropertyAccessInfo const & access_info,Node * receiver)196 Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
197     Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver) {
198   // Optimize immutable property loads.
199   HeapObjectMatcher m(receiver);
200   if (m.HasValue() && m.Value()->IsJSObject()) {
201     // TODO(ishell): Use something simpler like
202     //
203     // Handle<Object> value =
204     //     JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()),
205     //                              Representation::Tagged(), field_index);
206     //
207     // here, once we have the immutable bit in the access_info.
208 
209     // TODO(turbofan): Given that we already have the field_index here, we
210     // might be smarter in the future and not rely on the LookupIterator,
211     // but for now let's just do what Crankshaft does.
212     LookupIterator it(m.Value(), name, LookupIterator::OWN_SKIP_INTERCEPTOR);
213     if (it.state() == LookupIterator::DATA) {
214       bool is_reaonly_non_configurable =
215           it.IsReadOnly() && !it.IsConfigurable();
216       if (is_reaonly_non_configurable ||
217           (FLAG_track_constant_fields && access_info.IsDataConstantField())) {
218         Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
219         if (!is_reaonly_non_configurable) {
220           // It's necessary to add dependency on the map that introduced
221           // the field.
222           DCHECK(access_info.IsDataConstantField());
223           DCHECK(!it.is_dictionary_holder());
224           Handle<Map> field_owner_map = it.GetFieldOwnerMap();
225           dependencies()->AssumeFieldOwner(field_owner_map);
226         }
227         return value;
228       }
229     }
230   }
231   return nullptr;
232 }
233 
BuildLoadDataField(Handle<Name> name,PropertyAccessInfo const & access_info,Node * receiver,Node ** effect,Node ** control)234 Node* PropertyAccessBuilder::BuildLoadDataField(
235     Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver,
236     Node** effect, Node** control) {
237   DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
238   receiver = ResolveHolder(access_info, receiver);
239   if (Node* value =
240           TryBuildLoadConstantDataField(name, access_info, receiver)) {
241     return value;
242   }
243 
244   FieldIndex const field_index = access_info.field_index();
245   Type const field_type = access_info.field_type();
246   MachineRepresentation const field_representation =
247       access_info.field_representation();
248   Node* storage = receiver;
249   if (!field_index.is_inobject()) {
250     storage = *effect = graph()->NewNode(
251         simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
252         storage, *effect, *control);
253   }
254   FieldAccess field_access = {
255       kTaggedBase,
256       field_index.offset(),
257       name,
258       MaybeHandle<Map>(),
259       field_type,
260       MachineType::TypeForRepresentation(field_representation),
261       kFullWriteBarrier};
262   if (field_representation == MachineRepresentation::kFloat64) {
263     if (!field_index.is_inobject() || field_index.is_hidden_field() ||
264         !FLAG_unbox_double_fields) {
265       FieldAccess const storage_access = {kTaggedBase,
266                                           field_index.offset(),
267                                           name,
268                                           MaybeHandle<Map>(),
269                                           Type::OtherInternal(),
270                                           MachineType::TaggedPointer(),
271                                           kPointerWriteBarrier};
272       storage = *effect = graph()->NewNode(
273           simplified()->LoadField(storage_access), storage, *effect, *control);
274       field_access.offset = HeapNumber::kValueOffset;
275       field_access.name = MaybeHandle<Name>();
276     }
277   } else if (field_representation == MachineRepresentation::kTaggedPointer) {
278     // Remember the map of the field value, if its map is stable. This is
279     // used by the LoadElimination to eliminate map checks on the result.
280     Handle<Map> field_map;
281     if (access_info.field_map().ToHandle(&field_map)) {
282       if (field_map->is_stable()) {
283         dependencies()->AssumeMapStable(field_map);
284         field_access.map = field_map;
285       }
286     }
287   }
288   Node* value = *effect = graph()->NewNode(
289       simplified()->LoadField(field_access), storage, *effect, *control);
290   return value;
291 }
292 
293 }  // namespace compiler
294 }  // namespace internal
295 }  // namespace v8
296