OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/js-native-context-specialization.h" | 5 #include "src/compiler/js-native-context-specialization.h" |
6 | 6 |
7 #include "src/accessors.h" | 7 #include "src/accessors.h" |
8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
9 #include "src/compilation-dependencies.h" | 9 #include "src/compilation-dependencies.h" |
10 #include "src/compiler/access-builder.h" | 10 #include "src/compiler/access-builder.h" |
11 #include "src/compiler/access-info.h" | 11 #include "src/compiler/access-info.h" |
12 #include "src/compiler/js-graph.h" | 12 #include "src/compiler/js-graph.h" |
13 #include "src/compiler/js-operator.h" | 13 #include "src/compiler/js-operator.h" |
14 #include "src/compiler/linkage.h" | 14 #include "src/compiler/linkage.h" |
15 #include "src/compiler/node-matchers.h" | 15 #include "src/compiler/node-matchers.h" |
16 #include "src/field-index-inl.h" | 16 #include "src/field-index-inl.h" |
17 #include "src/isolate-inl.h" | 17 #include "src/isolate-inl.h" |
18 #include "src/type-cache.h" | 18 #include "src/type-cache.h" |
19 #include "src/type-feedback-vector.h" | 19 #include "src/type-feedback-vector.h" |
20 | 20 |
21 namespace v8 { | 21 namespace v8 { |
22 namespace internal { | 22 namespace internal { |
23 namespace compiler { | 23 namespace compiler { |
24 | 24 |
| 25 namespace { |
| 26 |
| 27 bool HasNumberMaps(MapList const& maps) { |
| 28 for (auto map : maps) { |
| 29 if (map->instance_type() == HEAP_NUMBER_TYPE) return true; |
| 30 } |
| 31 return false; |
| 32 } |
| 33 |
| 34 bool HasOnlyJSArrayMaps(MapList const& maps) { |
| 35 for (auto map : maps) { |
| 36 if (!map->IsJSArrayMap()) return false; |
| 37 } |
| 38 return true; |
| 39 } |
| 40 |
| 41 bool HasOnlyNumberMaps(MapList const& maps) { |
| 42 for (auto map : maps) { |
| 43 if (map->instance_type() != HEAP_NUMBER_TYPE) return false; |
| 44 } |
| 45 return true; |
| 46 } |
| 47 |
| 48 bool HasOnlyStringMaps(MapList const& maps) { |
| 49 for (auto map : maps) { |
| 50 if (!map->IsStringMap()) return false; |
| 51 } |
| 52 return true; |
| 53 } |
| 54 |
| 55 } // namespace |
| 56 |
25 JSNativeContextSpecialization::JSNativeContextSpecialization( | 57 JSNativeContextSpecialization::JSNativeContextSpecialization( |
26 Editor* editor, JSGraph* jsgraph, Flags flags, | 58 Editor* editor, JSGraph* jsgraph, Flags flags, |
27 MaybeHandle<Context> native_context, CompilationDependencies* dependencies, | 59 MaybeHandle<Context> native_context, CompilationDependencies* dependencies, |
28 Zone* zone) | 60 Zone* zone) |
29 : AdvancedReducer(editor), | 61 : AdvancedReducer(editor), |
30 jsgraph_(jsgraph), | 62 jsgraph_(jsgraph), |
31 flags_(flags), | 63 flags_(flags), |
32 native_context_(native_context), | 64 native_context_(native_context), |
33 dependencies_(dependencies), | 65 dependencies_(dependencies), |
34 zone_(zone), | 66 zone_(zone), |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
93 graph()->zone()); | 125 graph()->zone()); |
94 ZoneVector<PropertyAccessInfo> access_infos(zone()); | 126 ZoneVector<PropertyAccessInfo> access_infos(zone()); |
95 if (!access_info_factory.ComputePropertyAccessInfos( | 127 if (!access_info_factory.ComputePropertyAccessInfos( |
96 receiver_maps, name, access_mode, &access_infos)) { | 128 receiver_maps, name, access_mode, &access_infos)) { |
97 return NoChange(); | 129 return NoChange(); |
98 } | 130 } |
99 | 131 |
100 // Nothing to do if we have no non-deprecated maps. | 132 // Nothing to do if we have no non-deprecated maps. |
101 if (access_infos.empty()) return NoChange(); | 133 if (access_infos.empty()) return NoChange(); |
102 | 134 |
103 // The final states for every polymorphic branch. We join them with | |
104 // Merge++Phi+EffectPhi at the bottom. | |
105 ZoneVector<Node*> values(zone()); | |
106 ZoneVector<Node*> effects(zone()); | |
107 ZoneVector<Node*> controls(zone()); | |
108 | |
109 // Ensure that {index} matches the specified {name} (if {index} is given). | 135 // Ensure that {index} matches the specified {name} (if {index} is given). |
110 if (index != nullptr) { | 136 if (index != nullptr) { |
111 Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Name()), | 137 Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Name()), |
112 index, jsgraph()->HeapConstant(name)); | 138 index, jsgraph()->HeapConstant(name)); |
113 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); | 139 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); |
114 } | 140 } |
115 | 141 |
116 // Check if {receiver} may be a number. | 142 // Check for the monomorphic cases. |
117 bool receiverissmi_possible = false; | 143 if (access_infos.size() == 1 && |
118 for (PropertyAccessInfo const& access_info : access_infos) { | 144 HasOnlyStringMaps(access_infos[0].receiver_maps())) { |
119 if (access_info.receiver_type()->Is(Type::Number())) { | 145 // Monormorphic string access (ignoring the fact that there are multiple |
120 receiverissmi_possible = true; | 146 // String maps). |
121 break; | 147 receiver = effect = graph()->NewNode(simplified()->CheckString(), receiver, |
122 } | 148 effect, control); |
123 } | |
124 | |
125 // Ensure that {receiver} is a heap object. | |
126 Node* receiverissmi_control = nullptr; | |
127 Node* receiverissmi_effect = effect; | |
128 if (receiverissmi_possible) { | |
129 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | |
130 Node* branch = graph()->NewNode(common()->Branch(), check, control); | |
131 control = graph()->NewNode(common()->IfFalse(), branch); | |
132 receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch); | |
133 receiverissmi_effect = effect; | |
134 } else if (access_infos.size() != 1 || | |
135 !access_infos[0].receiver_type()->Is(Type::String())) { | |
136 // TODO(bmeurer): We omit the Smi check here if we are going to lower to | |
137 // the CheckString below; make this less horrible and adhoc. | |
138 receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(), | |
139 receiver, effect, control); | |
140 } | |
141 | |
142 // Load the {receiver} map. The resulting effect is the dominating effect for | |
143 // all (polymorphic) branches. | |
144 Node* receiver_map = effect = | |
145 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
146 receiver, effect, control); | |
147 | |
148 // Generate code for the various different property access patterns. | |
149 Node* fallthrough_control = control; | |
150 for (size_t j = 0; j < access_infos.size(); ++j) { | |
151 PropertyAccessInfo const& access_info = access_infos[j]; | |
152 Node* this_value = value; | |
153 Node* this_receiver = receiver; | |
154 Node* this_effect = effect; | |
155 Node* this_control; | |
156 | |
157 // Perform map check on {receiver}. | |
158 Type* receiver_type = access_info.receiver_type(); | |
159 if (receiver_type->Is(Type::String())) { | |
160 if (j == access_infos.size() - 1) { | |
161 this_receiver = this_effect = | |
162 graph()->NewNode(simplified()->CheckString(), receiver, this_effect, | |
163 fallthrough_control); | |
164 this_control = fallthrough_control; | |
165 fallthrough_control = nullptr; | |
166 } else { | |
167 Node* check = | |
168 graph()->NewNode(simplified()->ObjectIsString(), receiver); | |
169 Node* branch = | |
170 graph()->NewNode(common()->Branch(), check, fallthrough_control); | |
171 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
172 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
173 } | |
174 } else { | |
175 // Emit a (sequence of) map checks for other {receiver}s. | |
176 ZoneVector<Node*> this_controls(zone()); | |
177 ZoneVector<Node*> this_effects(zone()); | |
178 int num_classes = access_info.receiver_type()->NumClasses(); | |
179 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | |
180 i.Advance()) { | |
181 DCHECK_LT(0, num_classes); | |
182 Handle<Map> map = i.Current(); | |
183 Node* check = | |
184 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
185 receiver_map, jsgraph()->Constant(map)); | |
186 if (--num_classes == 0 && j == access_infos.size() - 1) { | |
187 check = graph()->NewNode(simplified()->CheckIf(), check, this_effect, | |
188 fallthrough_control); | |
189 this_controls.push_back(fallthrough_control); | |
190 this_effects.push_back(check); | |
191 fallthrough_control = nullptr; | |
192 } else { | |
193 Node* branch = | |
194 graph()->NewNode(common()->Branch(), check, fallthrough_control); | |
195 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
196 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
197 this_effects.push_back(this_effect); | |
198 } | |
199 } | |
200 | |
201 // The Number case requires special treatment to also deal with Smis. | |
202 if (receiver_type->Is(Type::Number())) { | |
203 // Join this check with the "receiver is smi" check above. | |
204 DCHECK_NOT_NULL(receiverissmi_effect); | |
205 DCHECK_NOT_NULL(receiverissmi_control); | |
206 this_effects.push_back(receiverissmi_effect); | |
207 this_controls.push_back(receiverissmi_control); | |
208 receiverissmi_effect = receiverissmi_control = nullptr; | |
209 } | |
210 | |
211 // Create dominating Merge+EffectPhi for this {receiver} type. | |
212 int const this_control_count = static_cast<int>(this_controls.size()); | |
213 this_control = | |
214 (this_control_count == 1) | |
215 ? this_controls.front() | |
216 : graph()->NewNode(common()->Merge(this_control_count), | |
217 this_control_count, &this_controls.front()); | |
218 this_effects.push_back(this_control); | |
219 int const this_effect_count = static_cast<int>(this_effects.size()); | |
220 this_effect = | |
221 (this_control_count == 1) | |
222 ? this_effects.front() | |
223 : graph()->NewNode(common()->EffectPhi(this_control_count), | |
224 this_effect_count, &this_effects.front()); | |
225 } | |
226 | |
227 // Determine actual holder and perform prototype chain checks. | |
228 Handle<JSObject> holder; | |
229 if (access_info.holder().ToHandle(&holder)) { | |
230 AssumePrototypesStable(receiver_type, native_context, holder); | |
231 } | |
232 | 149 |
233 // Generate the actual property access. | 150 // Generate the actual property access. |
234 if (access_info.IsNotFound()) { | 151 ValueEffectControl continuation = |
235 DCHECK_EQ(AccessMode::kLoad, access_mode); | 152 BuildPropertyAccess(receiver, value, effect, control, name, |
236 this_value = jsgraph()->UndefinedConstant(); | 153 native_context, access_infos[0], access_mode); |
237 } else if (access_info.IsDataConstant()) { | 154 value = continuation.value(); |
238 this_value = jsgraph()->Constant(access_info.constant()); | 155 effect = continuation.effect(); |
239 if (access_mode == AccessMode::kStore) { | 156 control = continuation.control(); |
240 Node* check = graph()->NewNode( | 157 } else if (access_infos.size() == 1 && |
241 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); | 158 HasOnlyNumberMaps(access_infos[0].receiver_maps())) { |
242 this_effect = graph()->NewNode(simplified()->CheckIf(), check, | 159 // Monomorphic number access (we also deal with Smis here). |
243 this_effect, this_control); | 160 receiver = effect = graph()->NewNode(simplified()->CheckNumber(), receiver, |
244 } | 161 effect, control); |
245 } else { | |
246 DCHECK(access_info.IsDataField()); | |
247 FieldIndex const field_index = access_info.field_index(); | |
248 Type* const field_type = access_info.field_type(); | |
249 if (access_mode == AccessMode::kLoad && | |
250 access_info.holder().ToHandle(&holder)) { | |
251 this_receiver = jsgraph()->Constant(holder); | |
252 } | |
253 Node* this_storage = this_receiver; | |
254 if (!field_index.is_inobject()) { | |
255 this_storage = this_effect = graph()->NewNode( | |
256 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | |
257 this_storage, this_effect, this_control); | |
258 } | |
259 FieldAccess field_access = { | |
260 kTaggedBase, field_index.offset(), name, | |
261 field_type, MachineType::AnyTagged(), kFullWriteBarrier}; | |
262 if (access_mode == AccessMode::kLoad) { | |
263 if (field_type->Is(Type::UntaggedFloat64())) { | |
264 if (!field_index.is_inobject() || field_index.is_hidden_field() || | |
265 !FLAG_unbox_double_fields) { | |
266 this_storage = this_effect = | |
267 graph()->NewNode(simplified()->LoadField(field_access), | |
268 this_storage, this_effect, this_control); | |
269 field_access.offset = HeapNumber::kValueOffset; | |
270 field_access.name = MaybeHandle<Name>(); | |
271 } | |
272 field_access.machine_type = MachineType::Float64(); | |
273 } | |
274 this_value = this_effect = | |
275 graph()->NewNode(simplified()->LoadField(field_access), | |
276 this_storage, this_effect, this_control); | |
277 } else { | |
278 DCHECK_EQ(AccessMode::kStore, access_mode); | |
279 if (field_type->Is(Type::UntaggedFloat64())) { | |
280 this_value = this_effect = | |
281 graph()->NewNode(simplified()->CheckNumber(), this_value, | |
282 this_effect, this_control); | |
283 | 162 |
284 if (!field_index.is_inobject() || field_index.is_hidden_field() || | 163 // Generate the actual property access. |
285 !FLAG_unbox_double_fields) { | 164 ValueEffectControl continuation = |
286 if (access_info.HasTransitionMap()) { | 165 BuildPropertyAccess(receiver, value, effect, control, name, |
287 // Allocate a MutableHeapNumber for the new property. | 166 native_context, access_infos[0], access_mode); |
288 this_effect = graph()->NewNode( | 167 value = continuation.value(); |
289 common()->BeginRegion(RegionObservability::kNotObservable), | 168 effect = continuation.effect(); |
290 this_effect); | 169 control = continuation.control(); |
291 Node* this_box = this_effect = | 170 } else { |
292 graph()->NewNode(simplified()->Allocate(NOT_TENURED), | 171 // The final states for every polymorphic branch. We join them with |
293 jsgraph()->Constant(HeapNumber::kSize), | 172 // Merge+Phi+EffectPhi at the bottom. |
294 this_effect, this_control); | 173 ZoneVector<Node*> values(zone()); |
295 this_effect = graph()->NewNode( | 174 ZoneVector<Node*> effects(zone()); |
296 simplified()->StoreField(AccessBuilder::ForMap()), this_box, | 175 ZoneVector<Node*> controls(zone()); |
297 jsgraph()->HeapConstant(factory()->mutable_heap_number_map()), | |
298 this_effect, this_control); | |
299 this_effect = graph()->NewNode( | |
300 simplified()->StoreField(AccessBuilder::ForHeapNumberValue()), | |
301 this_box, this_value, this_effect, this_control); | |
302 this_value = this_effect = graph()->NewNode( | |
303 common()->FinishRegion(), this_box, this_effect); | |
304 | 176 |
305 field_access.type = Type::TaggedPointer(); | 177 // Check if {receiver} may be a number. |
306 } else { | 178 bool receiverissmi_possible = false; |
307 // We just store directly to the MutableHeapNumber. | 179 for (PropertyAccessInfo const& access_info : access_infos) { |
308 this_storage = this_effect = | 180 if (HasNumberMaps(access_info.receiver_maps())) { |
309 graph()->NewNode(simplified()->LoadField(field_access), | 181 receiverissmi_possible = true; |
310 this_storage, this_effect, this_control); | 182 break; |
311 field_access.offset = HeapNumber::kValueOffset; | |
312 field_access.name = MaybeHandle<Name>(); | |
313 field_access.machine_type = MachineType::Float64(); | |
314 } | |
315 } else { | |
316 // Unboxed double field, we store directly to the field. | |
317 field_access.machine_type = MachineType::Float64(); | |
318 } | |
319 } else if (field_type->Is(Type::TaggedSigned())) { | |
320 this_value = this_effect = | |
321 graph()->NewNode(simplified()->CheckTaggedSigned(), this_value, | |
322 this_effect, this_control); | |
323 } else if (field_type->Is(Type::TaggedPointer())) { | |
324 this_value = this_effect = | |
325 graph()->NewNode(simplified()->CheckTaggedPointer(), this_value, | |
326 this_effect, this_control); | |
327 if (field_type->NumClasses() == 1) { | |
328 // Emit a map check for the value. | |
329 Node* this_value_map = this_effect = graph()->NewNode( | |
330 simplified()->LoadField(AccessBuilder::ForMap()), this_value, | |
331 this_effect, this_control); | |
332 Node* check = graph()->NewNode( | |
333 simplified()->ReferenceEqual(Type::Internal()), this_value_map, | |
334 jsgraph()->Constant(field_type->Classes().Current())); | |
335 this_effect = graph()->NewNode(simplified()->CheckIf(), check, | |
336 this_effect, this_control); | |
337 } else { | |
338 DCHECK_EQ(0, field_type->NumClasses()); | |
339 } | |
340 } else { | |
341 DCHECK(field_type->Is(Type::Tagged())); | |
342 } | |
343 Handle<Map> transition_map; | |
344 if (access_info.transition_map().ToHandle(&transition_map)) { | |
345 this_effect = graph()->NewNode( | |
346 common()->BeginRegion(RegionObservability::kObservable), | |
347 this_effect); | |
348 this_effect = graph()->NewNode( | |
349 simplified()->StoreField(AccessBuilder::ForMap()), this_receiver, | |
350 jsgraph()->Constant(transition_map), this_effect, this_control); | |
351 } | |
352 this_effect = graph()->NewNode(simplified()->StoreField(field_access), | |
353 this_storage, this_value, this_effect, | |
354 this_control); | |
355 if (access_info.HasTransitionMap()) { | |
356 this_effect = | |
357 graph()->NewNode(common()->FinishRegion(), | |
358 jsgraph()->UndefinedConstant(), this_effect); | |
359 } | |
360 } | 183 } |
361 } | 184 } |
362 | 185 |
363 // Remember the final state for this property access. | 186 // Ensure that {receiver} is a heap object. |
364 values.push_back(this_value); | 187 Node* receiverissmi_control = nullptr; |
365 effects.push_back(this_effect); | 188 Node* receiverissmi_effect = effect; |
366 controls.push_back(this_control); | 189 if (receiverissmi_possible) { |
367 } | 190 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); |
| 191 Node* branch = graph()->NewNode(common()->Branch(), check, control); |
| 192 control = graph()->NewNode(common()->IfFalse(), branch); |
| 193 receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch); |
| 194 receiverissmi_effect = effect; |
| 195 } else { |
| 196 receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(), |
| 197 receiver, effect, control); |
| 198 } |
368 | 199 |
369 DCHECK_NULL(fallthrough_control); | 200 // Load the {receiver} map. The resulting effect is the dominating effect |
| 201 // for all (polymorphic) branches. |
| 202 Node* receiver_map = effect = |
| 203 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 204 receiver, effect, control); |
370 | 205 |
371 // Generate the final merge point for all (polymorphic) branches. | 206 // Generate code for the various different property access patterns. |
372 int const control_count = static_cast<int>(controls.size()); | 207 Node* fallthrough_control = control; |
373 if (control_count == 0) { | 208 for (size_t j = 0; j < access_infos.size(); ++j) { |
374 value = effect = control = jsgraph()->Dead(); | 209 PropertyAccessInfo const& access_info = access_infos[j]; |
375 } else if (control_count == 1) { | 210 Node* this_value = value; |
376 value = values.front(); | 211 Node* this_receiver = receiver; |
377 effect = effects.front(); | 212 Node* this_effect = effect; |
378 control = controls.front(); | 213 Node* this_control; |
379 } else { | 214 |
380 control = graph()->NewNode(common()->Merge(control_count), control_count, | 215 // Perform map check on {receiver}. |
381 &controls.front()); | 216 MapList const& receiver_maps = access_info.receiver_maps(); |
382 values.push_back(control); | 217 { |
383 value = graph()->NewNode( | 218 // Emit a (sequence of) map checks for other {receiver}s. |
384 common()->Phi(MachineRepresentation::kTagged, control_count), | 219 ZoneVector<Node*> this_controls(zone()); |
385 control_count + 1, &values.front()); | 220 ZoneVector<Node*> this_effects(zone()); |
386 effects.push_back(control); | 221 size_t num_classes = receiver_maps.size(); |
387 effect = graph()->NewNode(common()->EffectPhi(control_count), | 222 for (auto map : receiver_maps) { |
388 control_count + 1, &effects.front()); | 223 DCHECK_LT(0u, num_classes); |
| 224 Node* check = |
| 225 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), |
| 226 receiver_map, jsgraph()->Constant(map)); |
| 227 if (--num_classes == 0 && j == access_infos.size() - 1) { |
| 228 check = graph()->NewNode(simplified()->CheckIf(), check, |
| 229 this_effect, fallthrough_control); |
| 230 this_controls.push_back(fallthrough_control); |
| 231 this_effects.push_back(check); |
| 232 fallthrough_control = nullptr; |
| 233 } else { |
| 234 Node* branch = graph()->NewNode(common()->Branch(), check, |
| 235 fallthrough_control); |
| 236 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
| 237 this_controls.push_back( |
| 238 graph()->NewNode(common()->IfTrue(), branch)); |
| 239 this_effects.push_back(this_effect); |
| 240 } |
| 241 } |
| 242 |
| 243 // The Number case requires special treatment to also deal with Smis. |
| 244 if (HasNumberMaps(receiver_maps)) { |
| 245 // Join this check with the "receiver is smi" check above. |
| 246 DCHECK_NOT_NULL(receiverissmi_effect); |
| 247 DCHECK_NOT_NULL(receiverissmi_control); |
| 248 this_effects.push_back(receiverissmi_effect); |
| 249 this_controls.push_back(receiverissmi_control); |
| 250 receiverissmi_effect = receiverissmi_control = nullptr; |
| 251 } |
| 252 |
| 253 // Create dominating Merge+EffectPhi for this {receiver} type. |
| 254 int const this_control_count = static_cast<int>(this_controls.size()); |
| 255 this_control = |
| 256 (this_control_count == 1) |
| 257 ? this_controls.front() |
| 258 : graph()->NewNode(common()->Merge(this_control_count), |
| 259 this_control_count, &this_controls.front()); |
| 260 this_effects.push_back(this_control); |
| 261 int const this_effect_count = static_cast<int>(this_effects.size()); |
| 262 this_effect = |
| 263 (this_control_count == 1) |
| 264 ? this_effects.front() |
| 265 : graph()->NewNode(common()->EffectPhi(this_control_count), |
| 266 this_effect_count, &this_effects.front()); |
| 267 } |
| 268 |
| 269 // Generate the actual property access. |
| 270 ValueEffectControl continuation = BuildPropertyAccess( |
| 271 this_receiver, this_value, this_effect, this_control, name, |
| 272 native_context, access_info, access_mode); |
| 273 values.push_back(continuation.value()); |
| 274 effects.push_back(continuation.effect()); |
| 275 controls.push_back(continuation.control()); |
| 276 } |
| 277 |
| 278 DCHECK_NULL(fallthrough_control); |
| 279 |
| 280 // Generate the final merge point for all (polymorphic) branches. |
| 281 int const control_count = static_cast<int>(controls.size()); |
| 282 if (control_count == 0) { |
| 283 value = effect = control = jsgraph()->Dead(); |
| 284 } else if (control_count == 1) { |
| 285 value = values.front(); |
| 286 effect = effects.front(); |
| 287 control = controls.front(); |
| 288 } else { |
| 289 control = graph()->NewNode(common()->Merge(control_count), control_count, |
| 290 &controls.front()); |
| 291 values.push_back(control); |
| 292 value = graph()->NewNode( |
| 293 common()->Phi(MachineRepresentation::kTagged, control_count), |
| 294 control_count + 1, &values.front()); |
| 295 effects.push_back(control); |
| 296 effect = graph()->NewNode(common()->EffectPhi(control_count), |
| 297 control_count + 1, &effects.front()); |
| 298 } |
389 } | 299 } |
390 ReplaceWithValue(node, value, effect, control); | 300 ReplaceWithValue(node, value, effect, control); |
391 return Replace(value); | 301 return Replace(value); |
392 } | 302 } |
393 | 303 |
394 | |
395 Reduction JSNativeContextSpecialization::ReduceNamedAccess( | 304 Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
396 Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name, | 305 Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name, |
397 AccessMode access_mode, LanguageMode language_mode) { | 306 AccessMode access_mode, LanguageMode language_mode) { |
398 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || | 307 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || |
399 node->opcode() == IrOpcode::kJSStoreNamed); | 308 node->opcode() == IrOpcode::kJSStoreNamed); |
400 Node* const receiver = NodeProperties::GetValueInput(node, 0); | 309 Node* const receiver = NodeProperties::GetValueInput(node, 0); |
401 Node* const effect = NodeProperties::GetEffectInput(node); | 310 Node* const effect = NodeProperties::GetEffectInput(node); |
402 | 311 |
403 // Check if the {nexus} reports type feedback for the IC. | 312 // Check if the {nexus} reports type feedback for the IC. |
404 if (nexus.IsUninitialized()) { | 313 if (nexus.IsUninitialized()) { |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
553 jsgraph()->HeapConstant(transition_target), this_effect, | 462 jsgraph()->HeapConstant(transition_target), this_effect, |
554 this_control); | 463 this_control); |
555 } | 464 } |
556 | 465 |
557 // Load the {receiver} map. | 466 // Load the {receiver} map. |
558 Node* receiver_map = this_effect = | 467 Node* receiver_map = this_effect = |
559 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | 468 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
560 receiver, this_effect, this_control); | 469 receiver, this_effect, this_control); |
561 | 470 |
562 // Perform map check on {receiver}. | 471 // Perform map check on {receiver}. |
563 Type* receiver_type = access_info.receiver_type(); | 472 MapList const& receiver_maps = access_info.receiver_maps(); |
564 bool receiver_is_jsarray = true; | |
565 { | 473 { |
566 ZoneVector<Node*> this_controls(zone()); | 474 ZoneVector<Node*> this_controls(zone()); |
567 ZoneVector<Node*> this_effects(zone()); | 475 ZoneVector<Node*> this_effects(zone()); |
568 int num_classes = access_info.receiver_type()->NumClasses(); | 476 size_t num_classes = receiver_maps.size(); |
569 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | 477 for (Handle<Map> map : receiver_maps) { |
570 i.Advance()) { | 478 DCHECK_LT(0u, num_classes); |
571 DCHECK_LT(0, num_classes); | |
572 Handle<Map> map = i.Current(); | |
573 Node* check = | 479 Node* check = |
574 graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), | 480 graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), |
575 receiver_map, jsgraph()->Constant(map)); | 481 receiver_map, jsgraph()->Constant(map)); |
576 if (--num_classes == 0 && j == access_infos.size() - 1) { | 482 if (--num_classes == 0 && j == access_infos.size() - 1) { |
577 // Last map check on the fallthrough control path, do a conditional | 483 // Last map check on the fallthrough control path, do a conditional |
578 // eager deoptimization exit here. | 484 // eager deoptimization exit here. |
579 // TODO(turbofan): This is ugly as hell! We should probably introduce | 485 // TODO(turbofan): This is ugly as hell! We should probably introduce |
580 // macro-ish operators for property access that encapsulate this whole | 486 // macro-ish operators for property access that encapsulate this whole |
581 // mess. | 487 // mess. |
582 check = graph()->NewNode(simplified()->CheckIf(), check, this_effect, | 488 check = graph()->NewNode(simplified()->CheckIf(), check, this_effect, |
583 this_control); | 489 this_control); |
584 this_controls.push_back(this_control); | 490 this_controls.push_back(this_control); |
585 this_effects.push_back(check); | 491 this_effects.push_back(check); |
586 fallthrough_control = nullptr; | 492 fallthrough_control = nullptr; |
587 } else { | 493 } else { |
588 Node* branch = | 494 Node* branch = |
589 graph()->NewNode(common()->Branch(), check, fallthrough_control); | 495 graph()->NewNode(common()->Branch(), check, fallthrough_control); |
590 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | 496 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
591 this_effects.push_back(effect); | 497 this_effects.push_back(effect); |
592 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | 498 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
593 } | 499 } |
594 if (!map->IsJSArrayMap()) receiver_is_jsarray = false; | |
595 } | 500 } |
596 | 501 |
597 // Create single chokepoint for the control. | 502 // Create single chokepoint for the control. |
598 int const this_control_count = static_cast<int>(this_controls.size()); | 503 int const this_control_count = static_cast<int>(this_controls.size()); |
599 if (this_control_count == 1) { | 504 if (this_control_count == 1) { |
600 this_control = this_controls.front(); | 505 this_control = this_controls.front(); |
601 this_effect = this_effects.front(); | 506 this_effect = this_effects.front(); |
602 } else { | 507 } else { |
603 this_control = | 508 this_control = |
604 graph()->NewNode(common()->Merge(this_control_count), | 509 graph()->NewNode(common()->Merge(this_control_count), |
(...skipping 11 matching lines...) Expand all Loading... |
616 this_effect = graph()->NewNode(common()->Checkpoint(), frame_state, | 521 this_effect = graph()->NewNode(common()->Checkpoint(), frame_state, |
617 this_effect, this_control); | 522 this_effect, this_control); |
618 } | 523 } |
619 } | 524 } |
620 | 525 |
621 // Certain stores need a prototype chain check because shape changes | 526 // Certain stores need a prototype chain check because shape changes |
622 // could allow callbacks on elements in the prototype chain that are | 527 // could allow callbacks on elements in the prototype chain that are |
623 // not compatible with (monomorphic) keyed stores. | 528 // not compatible with (monomorphic) keyed stores. |
624 Handle<JSObject> holder; | 529 Handle<JSObject> holder; |
625 if (access_info.holder().ToHandle(&holder)) { | 530 if (access_info.holder().ToHandle(&holder)) { |
626 AssumePrototypesStable(receiver_type, native_context, holder); | 531 AssumePrototypesStable(receiver_maps, native_context, holder); |
627 } | 532 } |
628 | 533 |
629 // TODO(bmeurer): We currently specialize based on elements kind. We should | |
630 // also be able to properly support strings and other JSObjects here. | |
631 ElementsKind elements_kind = access_info.elements_kind(); | |
632 | |
633 // Load the elements for the {receiver}. | |
634 Node* this_elements = this_effect = graph()->NewNode( | |
635 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), | |
636 this_receiver, this_effect, this_control); | |
637 | |
638 // Don't try to store to a copy-on-write backing store. | |
639 if (access_mode == AccessMode::kStore && | |
640 IsFastSmiOrObjectElementsKind(elements_kind)) { | |
641 Node* this_elements_map = this_effect = | |
642 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
643 this_elements, this_effect, this_control); | |
644 Node* check = graph()->NewNode( | |
645 simplified()->ReferenceEqual(Type::Any()), this_elements_map, | |
646 jsgraph()->HeapConstant(factory()->fixed_array_map())); | |
647 this_effect = graph()->NewNode(simplified()->CheckIf(), check, | |
648 this_effect, this_control); | |
649 } | |
650 | |
651 // Load the length of the {receiver}. | |
652 Node* this_length = this_effect = | |
653 receiver_is_jsarray | |
654 ? graph()->NewNode( | |
655 simplified()->LoadField( | |
656 AccessBuilder::ForJSArrayLength(elements_kind)), | |
657 this_receiver, this_effect, this_control) | |
658 : graph()->NewNode( | |
659 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), | |
660 this_elements, this_effect, this_control); | |
661 | |
662 // Check that the {index} is in the valid range for the {receiver}. | |
663 this_index = this_effect = | |
664 graph()->NewNode(simplified()->CheckBounds(), this_index, this_length, | |
665 this_effect, this_control); | |
666 | |
667 // Compute the element access. | |
668 Type* element_type = Type::Any(); | |
669 MachineType element_machine_type = MachineType::AnyTagged(); | |
670 if (IsFastDoubleElementsKind(elements_kind)) { | |
671 element_type = Type::Number(); | |
672 element_machine_type = MachineType::Float64(); | |
673 } else if (IsFastSmiElementsKind(elements_kind)) { | |
674 element_type = type_cache_.kSmi; | |
675 } | |
676 ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize, | |
677 element_type, element_machine_type, | |
678 kFullWriteBarrier}; | |
679 | |
680 // Access the actual element. | 534 // Access the actual element. |
681 // TODO(bmeurer): Refactor this into separate methods or even a separate | 535 ValueEffectControl continuation = BuildElementAccess( |
682 // class that deals with the elements access. | 536 this_receiver, this_index, this_value, this_effect, this_control, |
683 if (access_mode == AccessMode::kLoad) { | 537 native_context, access_info, access_mode); |
684 // Compute the real element access type, which includes the hole in case | 538 values.push_back(continuation.value()); |
685 // of holey backing stores. | 539 effects.push_back(continuation.effect()); |
686 if (elements_kind == FAST_HOLEY_ELEMENTS || | 540 controls.push_back(continuation.control()); |
687 elements_kind == FAST_HOLEY_SMI_ELEMENTS) { | |
688 element_access.type = Type::Union( | |
689 element_type, | |
690 Type::Constant(factory()->the_hole_value(), graph()->zone()), | |
691 graph()->zone()); | |
692 } | |
693 // Perform the actual backing store access. | |
694 this_value = this_effect = graph()->NewNode( | |
695 simplified()->LoadElement(element_access), this_elements, this_index, | |
696 this_effect, this_control); | |
697 // Handle loading from holey backing stores correctly, by either mapping | |
698 // the hole to undefined if possible, or deoptimizing otherwise. | |
699 if (elements_kind == FAST_HOLEY_ELEMENTS || | |
700 elements_kind == FAST_HOLEY_SMI_ELEMENTS) { | |
701 // Perform the hole check on the result. | |
702 CheckTaggedHoleMode mode = CheckTaggedHoleMode::kNeverReturnHole; | |
703 // Check if we are allowed to turn the hole into undefined. | |
704 Type* initial_holey_array_type = Type::Class( | |
705 handle(isolate()->get_initial_js_array_map(elements_kind)), | |
706 graph()->zone()); | |
707 if (receiver_type->NowIs(initial_holey_array_type) && | |
708 isolate()->IsFastArrayConstructorPrototypeChainIntact()) { | |
709 // Add a code dependency on the array protector cell. | |
710 AssumePrototypesStable(receiver_type, native_context, | |
711 isolate()->initial_object_prototype()); | |
712 dependencies()->AssumePropertyCell(factory()->array_protector()); | |
713 // Turn the hole into undefined. | |
714 mode = CheckTaggedHoleMode::kConvertHoleToUndefined; | |
715 } | |
716 this_value = this_effect = | |
717 graph()->NewNode(simplified()->CheckTaggedHole(mode), this_value, | |
718 this_effect, this_control); | |
719 } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) { | |
720 // Perform the hole check on the result. | |
721 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole; | |
722 // Check if we are allowed to return the hole directly. | |
723 Type* initial_holey_array_type = Type::Class( | |
724 handle(isolate()->get_initial_js_array_map(elements_kind)), | |
725 graph()->zone()); | |
726 if (receiver_type->NowIs(initial_holey_array_type) && | |
727 isolate()->IsFastArrayConstructorPrototypeChainIntact()) { | |
728 // Add a code dependency on the array protector cell. | |
729 AssumePrototypesStable(receiver_type, native_context, | |
730 isolate()->initial_object_prototype()); | |
731 dependencies()->AssumePropertyCell(factory()->array_protector()); | |
732 // Return the signaling NaN hole directly if all uses are truncating. | |
733 mode = CheckFloat64HoleMode::kAllowReturnHole; | |
734 } | |
735 this_value = this_effect = | |
736 graph()->NewNode(simplified()->CheckFloat64Hole(mode), this_value, | |
737 this_effect, this_control); | |
738 } | |
739 } else { | |
740 DCHECK_EQ(AccessMode::kStore, access_mode); | |
741 if (IsFastSmiElementsKind(elements_kind)) { | |
742 this_value = this_effect = | |
743 graph()->NewNode(simplified()->CheckTaggedSigned(), this_value, | |
744 this_effect, this_control); | |
745 } else if (IsFastDoubleElementsKind(elements_kind)) { | |
746 this_value = this_effect = graph()->NewNode( | |
747 simplified()->CheckNumber(), this_value, this_effect, this_control); | |
748 // Make sure we do not store signalling NaNs into double arrays. | |
749 this_value = | |
750 graph()->NewNode(simplified()->NumberSilenceNaN(), this_value); | |
751 } | |
752 this_effect = graph()->NewNode(simplified()->StoreElement(element_access), | |
753 this_elements, this_index, this_value, | |
754 this_effect, this_control); | |
755 } | |
756 | |
757 // Remember the final state for this element access. | |
758 values.push_back(this_value); | |
759 effects.push_back(this_effect); | |
760 controls.push_back(this_control); | |
761 } | 541 } |
762 | 542 |
763 DCHECK_NULL(fallthrough_control); | 543 DCHECK_NULL(fallthrough_control); |
764 | 544 |
765 // Generate the final merge point for all (polymorphic) branches. | 545 // Generate the final merge point for all (polymorphic) branches. |
766 int const control_count = static_cast<int>(controls.size()); | 546 int const control_count = static_cast<int>(controls.size()); |
767 if (control_count == 0) { | 547 if (control_count == 0) { |
768 value = effect = control = jsgraph()->Dead(); | 548 value = effect = control = jsgraph()->Dead(); |
769 } else if (control_count == 1) { | 549 } else if (control_count == 1) { |
770 value = values.front(); | 550 value = values.front(); |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
897 KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 677 KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
898 | 678 |
899 // Extract the keyed access store mode from the KEYED_STORE_IC. | 679 // Extract the keyed access store mode from the KEYED_STORE_IC. |
900 KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode(); | 680 KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode(); |
901 | 681 |
902 // Try to lower the keyed access based on the {nexus}. | 682 // Try to lower the keyed access based on the {nexus}. |
903 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore, | 683 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore, |
904 p.language_mode(), store_mode); | 684 p.language_mode(), store_mode); |
905 } | 685 } |
906 | 686 |
| 687 JSNativeContextSpecialization::ValueEffectControl |
| 688 JSNativeContextSpecialization::BuildPropertyAccess( |
| 689 Node* receiver, Node* value, Node* effect, Node* control, Handle<Name> name, |
| 690 Handle<Context> native_context, PropertyAccessInfo const& access_info, |
| 691 AccessMode access_mode) { |
| 692 // Determine actual holder and perform prototype chain checks. |
| 693 Handle<JSObject> holder; |
| 694 if (access_info.holder().ToHandle(&holder)) { |
| 695 AssumePrototypesStable(access_info.receiver_maps(), native_context, holder); |
| 696 } |
| 697 |
| 698 // Generate the actual property access. |
| 699 if (access_info.IsNotFound()) { |
| 700 DCHECK_EQ(AccessMode::kLoad, access_mode); |
| 701 value = jsgraph()->UndefinedConstant(); |
| 702 } else if (access_info.IsDataConstant()) { |
| 703 value = jsgraph()->Constant(access_info.constant()); |
| 704 if (access_mode == AccessMode::kStore) { |
| 705 Node* check = graph()->NewNode( |
| 706 simplified()->ReferenceEqual(Type::Tagged()), value, value); |
| 707 effect = |
| 708 graph()->NewNode(simplified()->CheckIf(), check, effect, control); |
| 709 } |
| 710 } else { |
| 711 DCHECK(access_info.IsDataField()); |
| 712 FieldIndex const field_index = access_info.field_index(); |
| 713 Type* const field_type = access_info.field_type(); |
| 714 if (access_mode == AccessMode::kLoad && |
| 715 access_info.holder().ToHandle(&holder)) { |
| 716 receiver = jsgraph()->Constant(holder); |
| 717 } |
| 718 Node* storage = receiver; |
| 719 if (!field_index.is_inobject()) { |
| 720 storage = effect = graph()->NewNode( |
| 721 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), |
| 722 storage, effect, control); |
| 723 } |
| 724 FieldAccess field_access = { |
| 725 kTaggedBase, field_index.offset(), name, |
| 726 field_type, MachineType::AnyTagged(), kFullWriteBarrier}; |
| 727 if (access_mode == AccessMode::kLoad) { |
| 728 if (field_type->Is(Type::UntaggedFloat64())) { |
| 729 if (!field_index.is_inobject() || field_index.is_hidden_field() || |
| 730 !FLAG_unbox_double_fields) { |
| 731 storage = effect = graph()->NewNode( |
| 732 simplified()->LoadField(field_access), storage, effect, control); |
| 733 field_access.offset = HeapNumber::kValueOffset; |
| 734 field_access.name = MaybeHandle<Name>(); |
| 735 } |
| 736 field_access.machine_type = MachineType::Float64(); |
| 737 } |
| 738 value = effect = graph()->NewNode(simplified()->LoadField(field_access), |
| 739 storage, effect, control); |
| 740 } else { |
| 741 DCHECK_EQ(AccessMode::kStore, access_mode); |
| 742 if (field_type->Is(Type::UntaggedFloat64())) { |
| 743 value = effect = graph()->NewNode(simplified()->CheckNumber(), value, |
| 744 effect, control); |
| 745 |
| 746 if (!field_index.is_inobject() || field_index.is_hidden_field() || |
| 747 !FLAG_unbox_double_fields) { |
| 748 if (access_info.HasTransitionMap()) { |
| 749 // Allocate a MutableHeapNumber for the new property. |
| 750 effect = graph()->NewNode( |
| 751 common()->BeginRegion(RegionObservability::kNotObservable), |
| 752 effect); |
| 753 Node* box = effect = graph()->NewNode( |
| 754 simplified()->Allocate(NOT_TENURED), |
| 755 jsgraph()->Constant(HeapNumber::kSize), effect, control); |
| 756 effect = graph()->NewNode( |
| 757 simplified()->StoreField(AccessBuilder::ForMap()), box, |
| 758 jsgraph()->HeapConstant(factory()->mutable_heap_number_map()), |
| 759 effect, control); |
| 760 effect = graph()->NewNode( |
| 761 simplified()->StoreField(AccessBuilder::ForHeapNumberValue()), |
| 762 box, value, effect, control); |
| 763 value = effect = |
| 764 graph()->NewNode(common()->FinishRegion(), box, effect); |
| 765 |
| 766 field_access.type = Type::TaggedPointer(); |
| 767 } else { |
| 768 // We just store directly to the MutableHeapNumber. |
| 769 storage = effect = |
| 770 graph()->NewNode(simplified()->LoadField(field_access), storage, |
| 771 effect, control); |
| 772 field_access.offset = HeapNumber::kValueOffset; |
| 773 field_access.name = MaybeHandle<Name>(); |
| 774 field_access.machine_type = MachineType::Float64(); |
| 775 } |
| 776 } else { |
| 777 // Unboxed double field, we store directly to the field. |
| 778 field_access.machine_type = MachineType::Float64(); |
| 779 } |
| 780 } else if (field_type->Is(Type::TaggedSigned())) { |
| 781 value = effect = graph()->NewNode(simplified()->CheckTaggedSigned(), |
| 782 value, effect, control); |
| 783 } else if (field_type->Is(Type::TaggedPointer())) { |
| 784 value = effect = graph()->NewNode(simplified()->CheckTaggedPointer(), |
| 785 value, effect, control); |
| 786 if (field_type->NumClasses() == 1) { |
| 787 // Emit a map check for the value. |
| 788 Node* value_map = effect = |
| 789 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 790 value, effect, control); |
| 791 Node* check = graph()->NewNode( |
| 792 simplified()->ReferenceEqual(Type::Internal()), value_map, |
| 793 jsgraph()->Constant(field_type->Classes().Current())); |
| 794 effect = |
| 795 graph()->NewNode(simplified()->CheckIf(), check, effect, control); |
| 796 } else { |
| 797 DCHECK_EQ(0, field_type->NumClasses()); |
| 798 } |
| 799 } else { |
| 800 DCHECK(field_type->Is(Type::Tagged())); |
| 801 } |
| 802 Handle<Map> transition_map; |
| 803 if (access_info.transition_map().ToHandle(&transition_map)) { |
| 804 effect = graph()->NewNode( |
| 805 common()->BeginRegion(RegionObservability::kObservable), effect); |
| 806 effect = graph()->NewNode( |
| 807 simplified()->StoreField(AccessBuilder::ForMap()), receiver, |
| 808 jsgraph()->Constant(transition_map), effect, control); |
| 809 } |
| 810 effect = graph()->NewNode(simplified()->StoreField(field_access), storage, |
| 811 value, effect, control); |
| 812 if (access_info.HasTransitionMap()) { |
| 813 effect = graph()->NewNode(common()->FinishRegion(), |
| 814 jsgraph()->UndefinedConstant(), effect); |
| 815 } |
| 816 } |
| 817 } |
| 818 |
| 819 return ValueEffectControl(value, effect, control); |
| 820 } |
| 821 |
| 822 JSNativeContextSpecialization::ValueEffectControl |
| 823 JSNativeContextSpecialization::BuildElementAccess( |
| 824 Node* receiver, Node* index, Node* value, Node* effect, Node* control, |
| 825 Handle<Context> native_context, ElementAccessInfo const& access_info, |
| 826 AccessMode access_mode) { |
| 827 // Determine actual holder and perform prototype chain checks. |
| 828 Handle<JSObject> holder; |
| 829 if (access_info.holder().ToHandle(&holder)) { |
| 830 AssumePrototypesStable(access_info.receiver_maps(), native_context, holder); |
| 831 } |
| 832 |
| 833 // TODO(bmeurer): We currently specialize based on elements kind. We should |
| 834 // also be able to properly support strings and other JSObjects here. |
| 835 ElementsKind elements_kind = access_info.elements_kind(); |
| 836 MapList const& receiver_maps = access_info.receiver_maps(); |
| 837 |
| 838 // Load the elements for the {receiver}. |
| 839 Node* elements = effect = graph()->NewNode( |
| 840 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, |
| 841 effect, control); |
| 842 |
| 843 // Don't try to store to a copy-on-write backing store. |
| 844 if (access_mode == AccessMode::kStore && |
| 845 IsFastSmiOrObjectElementsKind(elements_kind)) { |
| 846 Node* elements_map = effect = |
| 847 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 848 elements, effect, control); |
| 849 Node* check = graph()->NewNode( |
| 850 simplified()->ReferenceEqual(Type::Any()), elements_map, |
| 851 jsgraph()->HeapConstant(factory()->fixed_array_map())); |
| 852 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); |
| 853 } |
| 854 |
| 855 // Load the length of the {receiver}. |
| 856 Node* length = effect = |
| 857 HasOnlyJSArrayMaps(receiver_maps) |
| 858 ? graph()->NewNode( |
| 859 simplified()->LoadField( |
| 860 AccessBuilder::ForJSArrayLength(elements_kind)), |
| 861 receiver, effect, control) |
| 862 : graph()->NewNode( |
| 863 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), |
| 864 elements, effect, control); |
| 865 |
| 866 // Check that the {index} is in the valid range for the {receiver}. |
| 867 index = effect = graph()->NewNode(simplified()->CheckBounds(), index, length, |
| 868 effect, control); |
| 869 |
| 870 // Compute the element access. |
| 871 Type* element_type = Type::Any(); |
| 872 MachineType element_machine_type = MachineType::AnyTagged(); |
| 873 if (IsFastDoubleElementsKind(elements_kind)) { |
| 874 element_type = Type::Number(); |
| 875 element_machine_type = MachineType::Float64(); |
| 876 } else if (IsFastSmiElementsKind(elements_kind)) { |
| 877 element_type = type_cache_.kSmi; |
| 878 } |
| 879 ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize, |
| 880 element_type, element_machine_type, |
| 881 kFullWriteBarrier}; |
| 882 |
| 883 // Access the actual element. |
| 884 // TODO(bmeurer): Refactor this into separate methods or even a separate |
| 885 // class that deals with the elements access. |
| 886 if (access_mode == AccessMode::kLoad) { |
| 887 // Compute the real element access type, which includes the hole in case |
| 888 // of holey backing stores. |
| 889 if (elements_kind == FAST_HOLEY_ELEMENTS || |
| 890 elements_kind == FAST_HOLEY_SMI_ELEMENTS) { |
| 891 element_access.type = Type::Union( |
| 892 element_type, |
| 893 Type::Constant(factory()->the_hole_value(), graph()->zone()), |
| 894 graph()->zone()); |
| 895 } |
| 896 // Perform the actual backing store access. |
| 897 value = effect = graph()->NewNode(simplified()->LoadElement(element_access), |
| 898 elements, index, effect, control); |
| 899 // Handle loading from holey backing stores correctly, by either mapping |
| 900 // the hole to undefined if possible, or deoptimizing otherwise. |
| 901 if (elements_kind == FAST_HOLEY_ELEMENTS || |
| 902 elements_kind == FAST_HOLEY_SMI_ELEMENTS) { |
| 903 // Perform the hole check on the result. |
| 904 CheckTaggedHoleMode mode = CheckTaggedHoleMode::kNeverReturnHole; |
| 905 // Check if we are allowed to turn the hole into undefined. |
| 906 // TODO(bmeurer): We might check the JSArray map from a different |
| 907 // context here; may need reinvestigation. |
| 908 if (receiver_maps.size() == 1 && |
| 909 receiver_maps[0].is_identical_to( |
| 910 handle(isolate()->get_initial_js_array_map(elements_kind))) && |
| 911 isolate()->IsFastArrayConstructorPrototypeChainIntact()) { |
| 912 // Add a code dependency on the array protector cell. |
| 913 dependencies()->AssumePrototypeMapsStable( |
| 914 receiver_maps[0], isolate()->initial_object_prototype()); |
| 915 dependencies()->AssumePropertyCell(factory()->array_protector()); |
| 916 // Turn the hole into undefined. |
| 917 mode = CheckTaggedHoleMode::kConvertHoleToUndefined; |
| 918 } |
| 919 value = effect = graph()->NewNode(simplified()->CheckTaggedHole(mode), |
| 920 value, effect, control); |
| 921 } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) { |
| 922 // Perform the hole check on the result. |
| 923 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole; |
| 924 // Check if we are allowed to return the hole directly. |
| 925 // TODO(bmeurer): We might check the JSArray map from a different |
| 926 // context here; may need reinvestigation. |
| 927 if (receiver_maps.size() == 1 && |
| 928 receiver_maps[0].is_identical_to( |
| 929 handle(isolate()->get_initial_js_array_map(elements_kind))) && |
| 930 isolate()->IsFastArrayConstructorPrototypeChainIntact()) { |
| 931 // Add a code dependency on the array protector cell. |
| 932 dependencies()->AssumePrototypeMapsStable( |
| 933 receiver_maps[0], isolate()->initial_object_prototype()); |
| 934 dependencies()->AssumePropertyCell(factory()->array_protector()); |
| 935 // Return the signaling NaN hole directly if all uses are truncating. |
| 936 mode = CheckFloat64HoleMode::kAllowReturnHole; |
| 937 } |
| 938 value = effect = graph()->NewNode(simplified()->CheckFloat64Hole(mode), |
| 939 value, effect, control); |
| 940 } |
| 941 } else { |
| 942 DCHECK_EQ(AccessMode::kStore, access_mode); |
| 943 if (IsFastSmiElementsKind(elements_kind)) { |
| 944 value = effect = graph()->NewNode(simplified()->CheckTaggedSigned(), |
| 945 value, effect, control); |
| 946 } else if (IsFastDoubleElementsKind(elements_kind)) { |
| 947 value = effect = |
| 948 graph()->NewNode(simplified()->CheckNumber(), value, effect, control); |
| 949 // Make sure we do not store signalling NaNs into double arrays. |
| 950 value = graph()->NewNode(simplified()->NumberSilenceNaN(), value); |
| 951 } |
| 952 effect = graph()->NewNode(simplified()->StoreElement(element_access), |
| 953 elements, index, value, effect, control); |
| 954 } |
| 955 |
| 956 return ValueEffectControl(value, effect, control); |
| 957 } |
907 | 958 |
908 void JSNativeContextSpecialization::AssumePrototypesStable( | 959 void JSNativeContextSpecialization::AssumePrototypesStable( |
909 Type* receiver_type, Handle<Context> native_context, | 960 std::vector<Handle<Map>> const& receiver_maps, |
910 Handle<JSObject> holder) { | 961 Handle<Context> native_context, Handle<JSObject> holder) { |
911 // Determine actual holder and perform prototype chain checks. | 962 // Determine actual holder and perform prototype chain checks. |
912 for (auto i = receiver_type->Classes(); !i.Done(); i.Advance()) { | 963 for (auto map : receiver_maps) { |
913 Handle<Map> map = i.Current(); | |
914 // Perform the implicit ToObject for primitives here. | 964 // Perform the implicit ToObject for primitives here. |
915 // Implemented according to ES6 section 7.3.2 GetV (V, P). | 965 // Implemented according to ES6 section 7.3.2 GetV (V, P). |
916 Handle<JSFunction> constructor; | 966 Handle<JSFunction> constructor; |
917 if (Map::GetConstructorFunction(map, native_context) | 967 if (Map::GetConstructorFunction(map, native_context) |
918 .ToHandle(&constructor)) { | 968 .ToHandle(&constructor)) { |
919 map = handle(constructor->initial_map(), isolate()); | 969 map = handle(constructor->initial_map(), isolate()); |
920 } | 970 } |
921 dependencies()->AssumePrototypeMapsStable(map, holder); | 971 dependencies()->AssumePrototypeMapsStable(map, holder); |
922 } | 972 } |
923 } | 973 } |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1039 } | 1089 } |
1040 | 1090 |
1041 | 1091 |
1042 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 1092 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
1043 return jsgraph()->simplified(); | 1093 return jsgraph()->simplified(); |
1044 } | 1094 } |
1045 | 1095 |
1046 } // namespace compiler | 1096 } // namespace compiler |
1047 } // namespace internal | 1097 } // namespace internal |
1048 } // namespace v8 | 1098 } // namespace v8 |
OLD | NEW |