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" |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
101 | 101 |
102 // Nothing to do if we have no non-deprecated maps. | 102 // Nothing to do if we have no non-deprecated maps. |
103 if (access_infos.empty()) return NoChange(); | 103 if (access_infos.empty()) return NoChange(); |
104 | 104 |
105 // The final states for every polymorphic branch. We join them with | 105 // The final states for every polymorphic branch. We join them with |
106 // Merge++Phi+EffectPhi at the bottom. | 106 // Merge++Phi+EffectPhi at the bottom. |
107 ZoneVector<Node*> values(zone()); | 107 ZoneVector<Node*> values(zone()); |
108 ZoneVector<Node*> effects(zone()); | 108 ZoneVector<Node*> effects(zone()); |
109 ZoneVector<Node*> controls(zone()); | 109 ZoneVector<Node*> controls(zone()); |
110 | 110 |
111 // The list of "exiting" controls, which currently go to a single deoptimize. | |
112 // TODO(bmeurer): Consider using an IC as fallback. | |
113 Node* const exit_effect = effect; | |
114 ZoneVector<Node*> exit_controls(zone()); | |
115 | |
116 // Ensure that {index} matches the specified {name} (if {index} is given). | 111 // Ensure that {index} matches the specified {name} (if {index} is given). |
117 if (index != nullptr) { | 112 if (index != nullptr) { |
118 Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Name()), | 113 Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Name()), |
119 index, jsgraph()->HeapConstant(name)); | 114 index, jsgraph()->HeapConstant(name)); |
120 Node* branch = | 115 control = graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state, |
121 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); | 116 effect, control); |
122 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | 117 } |
123 control = graph()->NewNode(common()->IfTrue(), branch); | 118 |
| 119 // Check if {receiver} may be a number. |
| 120 bool receiverissmi_possible = false; |
| 121 for (PropertyAccessInfo const& access_info : access_infos) { |
| 122 if (access_info.receiver_type()->Is(Type::Number())) { |
| 123 receiverissmi_possible = true; |
| 124 break; |
| 125 } |
124 } | 126 } |
125 | 127 |
126 // Ensure that {receiver} is a heap object. | 128 // Ensure that {receiver} is a heap object. |
127 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | 129 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); |
128 Node* branch = graph()->NewNode(common()->Branch(), check, control); | 130 Node* receiverissmi_control = nullptr; |
129 control = graph()->NewNode(common()->IfFalse(), branch); | |
130 Node* receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch); | |
131 Node* receiverissmi_effect = effect; | 131 Node* receiverissmi_effect = effect; |
| 132 if (receiverissmi_possible) { |
| 133 Node* branch = graph()->NewNode(common()->Branch(), check, control); |
| 134 control = graph()->NewNode(common()->IfFalse(), branch); |
| 135 receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch); |
| 136 receiverissmi_effect = effect; |
| 137 } else { |
| 138 control = graph()->NewNode(common()->DeoptimizeIf(), check, frame_state, |
| 139 effect, control); |
| 140 } |
132 | 141 |
133 // Load the {receiver} map. The resulting effect is the dominating effect for | 142 // Load the {receiver} map. The resulting effect is the dominating effect for |
134 // all (polymorphic) branches. | 143 // all (polymorphic) branches. |
135 Node* receiver_map = effect = | 144 Node* receiver_map = effect = |
136 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | 145 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
137 receiver, effect, control); | 146 receiver, effect, control); |
138 | 147 |
139 // Generate code for the various different property access patterns. | 148 // Generate code for the various different property access patterns. |
140 Node* fallthrough_control = control; | 149 Node* fallthrough_control = control; |
141 for (PropertyAccessInfo const& access_info : access_infos) { | 150 for (size_t j = 0; j < access_infos.size(); ++j) { |
| 151 PropertyAccessInfo const& access_info = access_infos[j]; |
142 Node* this_value = value; | 152 Node* this_value = value; |
143 Node* this_receiver = receiver; | 153 Node* this_receiver = receiver; |
144 Node* this_effect = effect; | 154 Node* this_effect = effect; |
145 Node* this_control; | 155 Node* this_control; |
146 | 156 |
147 // Perform map check on {receiver}. | 157 // Perform map check on {receiver}. |
148 Type* receiver_type = access_info.receiver_type(); | 158 Type* receiver_type = access_info.receiver_type(); |
149 if (receiver_type->Is(Type::String())) { | 159 if (receiver_type->Is(Type::String())) { |
150 // Emit an instance type check for strings. | 160 // Emit an instance type check for strings. |
151 Node* receiver_instance_type = this_effect = graph()->NewNode( | 161 Node* receiver_instance_type = this_effect = graph()->NewNode( |
152 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), | 162 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), |
153 receiver_map, this_effect, fallthrough_control); | 163 receiver_map, this_effect, fallthrough_control); |
154 Node* check = | 164 Node* check = |
155 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, | 165 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, |
156 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); | 166 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); |
157 Node* branch = | 167 if (j == access_infos.size() - 1) { |
158 graph()->NewNode(common()->Branch(), check, fallthrough_control); | 168 this_control = |
159 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | 169 graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state, |
160 this_control = graph()->NewNode(common()->IfTrue(), branch); | 170 this_effect, fallthrough_control); |
| 171 fallthrough_control = nullptr; |
| 172 } else { |
| 173 Node* branch = |
| 174 graph()->NewNode(common()->Branch(), check, fallthrough_control); |
| 175 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
| 176 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 177 } |
161 } else { | 178 } else { |
162 // Emit a (sequence of) map checks for other {receiver}s. | 179 // Emit a (sequence of) map checks for other {receiver}s. |
163 ZoneVector<Node*> this_controls(zone()); | 180 ZoneVector<Node*> this_controls(zone()); |
164 ZoneVector<Node*> this_effects(zone()); | 181 ZoneVector<Node*> this_effects(zone()); |
| 182 int num_classes = access_info.receiver_type()->NumClasses(); |
165 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | 183 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); |
166 i.Advance()) { | 184 i.Advance()) { |
| 185 DCHECK_LT(0, num_classes); |
167 Handle<Map> map = i.Current(); | 186 Handle<Map> map = i.Current(); |
168 Node* check = | 187 Node* check = |
169 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | 188 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), |
170 receiver_map, jsgraph()->Constant(map)); | 189 receiver_map, jsgraph()->Constant(map)); |
171 Node* branch = | 190 if (--num_classes == 0 && j == access_infos.size() - 1) { |
172 graph()->NewNode(common()->Branch(), check, fallthrough_control); | 191 this_controls.push_back( |
173 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | 192 graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state, |
174 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | 193 this_effect, fallthrough_control)); |
175 this_effects.push_back(this_effect); | 194 this_effects.push_back(this_effect); |
| 195 fallthrough_control = nullptr; |
| 196 } else { |
| 197 Node* branch = |
| 198 graph()->NewNode(common()->Branch(), check, fallthrough_control); |
| 199 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
| 200 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
| 201 this_effects.push_back(this_effect); |
| 202 } |
176 } | 203 } |
177 | 204 |
178 // The Number case requires special treatment to also deal with Smis. | 205 // The Number case requires special treatment to also deal with Smis. |
179 if (receiver_type->Is(Type::Number())) { | 206 if (receiver_type->Is(Type::Number())) { |
180 // Join this check with the "receiver is smi" check above, and mark the | 207 // Join this check with the "receiver is smi" check above. |
181 // "receiver is smi" check as "consumed" so that we don't deoptimize if | 208 DCHECK_NOT_NULL(receiverissmi_effect); |
182 // the {receiver} is actually a Smi. | 209 DCHECK_NOT_NULL(receiverissmi_control); |
183 if (receiverissmi_control != nullptr) { | 210 this_effects.push_back(receiverissmi_effect); |
184 this_controls.push_back(receiverissmi_control); | 211 this_controls.push_back(receiverissmi_control); |
185 this_effects.push_back(receiverissmi_effect); | 212 receiverissmi_effect = receiverissmi_control = nullptr; |
186 receiverissmi_control = receiverissmi_effect = nullptr; | |
187 } | |
188 } | 213 } |
189 | 214 |
190 // Create dominating Merge+EffectPhi for this {receiver} type. | 215 // Create dominating Merge+EffectPhi for this {receiver} type. |
191 int const this_control_count = static_cast<int>(this_controls.size()); | 216 int const this_control_count = static_cast<int>(this_controls.size()); |
192 this_control = | 217 this_control = |
193 (this_control_count == 1) | 218 (this_control_count == 1) |
194 ? this_controls.front() | 219 ? this_controls.front() |
195 : graph()->NewNode(common()->Merge(this_control_count), | 220 : graph()->NewNode(common()->Merge(this_control_count), |
196 this_control_count, &this_controls.front()); | 221 this_control_count, &this_controls.front()); |
197 this_effects.push_back(this_control); | 222 this_effects.push_back(this_control); |
198 int const this_effect_count = static_cast<int>(this_effects.size()); | 223 int const this_effect_count = static_cast<int>(this_effects.size()); |
199 this_effect = | 224 this_effect = |
200 (this_control_count == 1) | 225 (this_control_count == 1) |
201 ? this_effects.front() | 226 ? this_effects.front() |
202 : graph()->NewNode(common()->EffectPhi(this_control_count), | 227 : graph()->NewNode(common()->EffectPhi(this_control_count), |
203 this_effect_count, &this_effects.front()); | 228 this_effect_count, &this_effects.front()); |
204 } | 229 } |
205 | 230 |
206 // Determine actual holder and perform prototype chain checks. | 231 // Determine actual holder and perform prototype chain checks. |
207 Handle<JSObject> holder; | 232 Handle<JSObject> holder; |
208 if (access_info.holder().ToHandle(&holder)) { | 233 if (access_info.holder().ToHandle(&holder)) { |
209 AssumePrototypesStable(receiver_type, native_context, holder); | 234 AssumePrototypesStable(receiver_type, native_context, holder); |
210 } | 235 } |
211 | 236 |
212 // Generate the actual property access. | 237 // Generate the actual property access. |
213 if (access_info.IsNotFound()) { | 238 if (access_info.IsNotFound()) { |
214 DCHECK_EQ(AccessMode::kLoad, access_mode); | 239 DCHECK_EQ(AccessMode::kLoad, access_mode); |
215 if (is_strong(language_mode)) { | 240 this_value = jsgraph()->UndefinedConstant(); |
216 // TODO(bmeurer/mstarzinger): Add support for lowering inside try | |
217 // blocks rewiring the IfException edge to a runtime call/throw. | |
218 exit_controls.push_back(this_control); | |
219 continue; | |
220 } else { | |
221 this_value = jsgraph()->UndefinedConstant(); | |
222 } | |
223 } else if (access_info.IsDataConstant()) { | 241 } else if (access_info.IsDataConstant()) { |
224 this_value = jsgraph()->Constant(access_info.constant()); | 242 this_value = jsgraph()->Constant(access_info.constant()); |
225 if (access_mode == AccessMode::kStore) { | 243 if (access_mode == AccessMode::kStore) { |
226 Node* check = graph()->NewNode( | 244 Node* check = graph()->NewNode( |
227 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); | 245 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); |
228 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 246 this_control = graph()->NewNode(common()->DeoptimizeUnless(), check, |
229 check, this_control); | 247 frame_state, this_effect, this_control); |
230 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
231 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
232 } | 248 } |
233 } else { | 249 } else { |
234 DCHECK(access_info.IsDataField()); | 250 DCHECK(access_info.IsDataField()); |
235 FieldIndex const field_index = access_info.field_index(); | 251 FieldIndex const field_index = access_info.field_index(); |
236 FieldCheck const field_check = access_info.field_check(); | 252 FieldCheck const field_check = access_info.field_check(); |
237 Type* const field_type = access_info.field_type(); | 253 Type* const field_type = access_info.field_type(); |
238 switch (field_check) { | 254 switch (field_check) { |
239 case FieldCheck::kNone: | 255 case FieldCheck::kNone: |
240 break; | 256 break; |
241 case FieldCheck::kJSArrayBufferViewBufferNotNeutered: { | 257 case FieldCheck::kJSArrayBufferViewBufferNotNeutered: { |
242 Node* this_buffer = this_effect = | 258 Node* this_buffer = this_effect = |
243 graph()->NewNode(simplified()->LoadField( | 259 graph()->NewNode(simplified()->LoadField( |
244 AccessBuilder::ForJSArrayBufferViewBuffer()), | 260 AccessBuilder::ForJSArrayBufferViewBuffer()), |
245 this_receiver, this_effect, this_control); | 261 this_receiver, this_effect, this_control); |
246 Node* this_buffer_bit_field = this_effect = | 262 Node* this_buffer_bit_field = this_effect = |
247 graph()->NewNode(simplified()->LoadField( | 263 graph()->NewNode(simplified()->LoadField( |
248 AccessBuilder::ForJSArrayBufferBitField()), | 264 AccessBuilder::ForJSArrayBufferBitField()), |
249 this_buffer, this_effect, this_control); | 265 this_buffer, this_effect, this_control); |
250 Node* check = graph()->NewNode( | 266 Node* check = graph()->NewNode( |
251 machine()->Word32Equal(), | 267 machine()->Word32Equal(), |
252 graph()->NewNode(machine()->Word32And(), this_buffer_bit_field, | 268 graph()->NewNode(machine()->Word32And(), this_buffer_bit_field, |
253 jsgraph()->Int32Constant( | 269 jsgraph()->Int32Constant( |
254 1 << JSArrayBuffer::WasNeutered::kShift)), | 270 1 << JSArrayBuffer::WasNeutered::kShift)), |
255 jsgraph()->Int32Constant(0)); | 271 jsgraph()->Int32Constant(0)); |
256 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), | 272 this_control = |
257 check, this_control); | 273 graph()->NewNode(common()->DeoptimizeIf(), check, frame_state, |
258 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | 274 this_effect, this_control); |
259 this_control = graph()->NewNode(common()->IfFalse(), branch); | |
260 break; | 275 break; |
261 } | 276 } |
262 } | 277 } |
263 if (access_mode == AccessMode::kLoad && | 278 if (access_mode == AccessMode::kLoad && |
264 access_info.holder().ToHandle(&holder)) { | 279 access_info.holder().ToHandle(&holder)) { |
265 this_receiver = jsgraph()->Constant(holder); | 280 this_receiver = jsgraph()->Constant(holder); |
266 } | 281 } |
267 Node* this_storage = this_receiver; | 282 Node* this_storage = this_receiver; |
268 if (!field_index.is_inobject()) { | 283 if (!field_index.is_inobject()) { |
269 this_storage = this_effect = graph()->NewNode( | 284 this_storage = this_effect = graph()->NewNode( |
(...skipping 15 matching lines...) Expand all Loading... |
285 field_access.machine_type = MachineType::Float64(); | 300 field_access.machine_type = MachineType::Float64(); |
286 } | 301 } |
287 this_value = this_effect = | 302 this_value = this_effect = |
288 graph()->NewNode(simplified()->LoadField(field_access), | 303 graph()->NewNode(simplified()->LoadField(field_access), |
289 this_storage, this_effect, this_control); | 304 this_storage, this_effect, this_control); |
290 } else { | 305 } else { |
291 DCHECK_EQ(AccessMode::kStore, access_mode); | 306 DCHECK_EQ(AccessMode::kStore, access_mode); |
292 if (field_type->Is(Type::UntaggedFloat64())) { | 307 if (field_type->Is(Type::UntaggedFloat64())) { |
293 Node* check = | 308 Node* check = |
294 graph()->NewNode(simplified()->ObjectIsNumber(), this_value); | 309 graph()->NewNode(simplified()->ObjectIsNumber(), this_value); |
295 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 310 this_control = |
296 check, this_control); | 311 graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state, |
297 exit_controls.push_back( | 312 this_effect, this_control); |
298 graph()->NewNode(common()->IfFalse(), branch)); | |
299 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
300 this_value = graph()->NewNode(common()->Guard(Type::Number()), | 313 this_value = graph()->NewNode(common()->Guard(Type::Number()), |
301 this_value, this_control); | 314 this_value, this_control); |
302 | 315 |
303 if (!field_index.is_inobject() || field_index.is_hidden_field() || | 316 if (!field_index.is_inobject() || field_index.is_hidden_field() || |
304 !FLAG_unbox_double_fields) { | 317 !FLAG_unbox_double_fields) { |
305 if (access_info.HasTransitionMap()) { | 318 if (access_info.HasTransitionMap()) { |
306 // Allocate a MutableHeapNumber for the new property. | 319 // Allocate a MutableHeapNumber for the new property. |
307 Callable callable = | 320 Callable callable = |
308 CodeFactory::AllocateMutableHeapNumber(isolate()); | 321 CodeFactory::AllocateMutableHeapNumber(isolate()); |
309 CallDescriptor* desc = Linkage::GetStubCallDescriptor( | 322 CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
(...skipping 18 matching lines...) Expand all Loading... |
328 field_access.name = MaybeHandle<Name>(); | 341 field_access.name = MaybeHandle<Name>(); |
329 field_access.machine_type = MachineType::Float64(); | 342 field_access.machine_type = MachineType::Float64(); |
330 } | 343 } |
331 } else { | 344 } else { |
332 // Unboxed double field, we store directly to the field. | 345 // Unboxed double field, we store directly to the field. |
333 field_access.machine_type = MachineType::Float64(); | 346 field_access.machine_type = MachineType::Float64(); |
334 } | 347 } |
335 } else if (field_type->Is(Type::TaggedSigned())) { | 348 } else if (field_type->Is(Type::TaggedSigned())) { |
336 Node* check = | 349 Node* check = |
337 graph()->NewNode(simplified()->ObjectIsSmi(), this_value); | 350 graph()->NewNode(simplified()->ObjectIsSmi(), this_value); |
338 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 351 this_control = |
339 check, this_control); | 352 graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state, |
340 exit_controls.push_back( | 353 this_effect, this_control); |
341 graph()->NewNode(common()->IfFalse(), branch)); | |
342 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
343 this_value = graph()->NewNode(common()->Guard(type_cache_.kSmi), | 354 this_value = graph()->NewNode(common()->Guard(type_cache_.kSmi), |
344 this_value, this_control); | 355 this_value, this_control); |
345 } else if (field_type->Is(Type::TaggedPointer())) { | 356 } else if (field_type->Is(Type::TaggedPointer())) { |
346 Node* check = | 357 Node* check = |
347 graph()->NewNode(simplified()->ObjectIsSmi(), this_value); | 358 graph()->NewNode(simplified()->ObjectIsSmi(), this_value); |
348 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), | 359 this_control = |
349 check, this_control); | 360 graph()->NewNode(common()->DeoptimizeIf(), check, frame_state, |
350 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | 361 this_effect, this_control); |
351 this_control = graph()->NewNode(common()->IfFalse(), branch); | 362 if (field_type->NumClasses() == 1) { |
352 if (field_type->NumClasses() > 0) { | 363 // Emit a map check for the value. |
353 // Emit a (sequence of) map checks for the value. | |
354 ZoneVector<Node*> this_controls(zone()); | |
355 Node* this_value_map = this_effect = graph()->NewNode( | 364 Node* this_value_map = this_effect = graph()->NewNode( |
356 simplified()->LoadField(AccessBuilder::ForMap()), this_value, | 365 simplified()->LoadField(AccessBuilder::ForMap()), this_value, |
357 this_effect, this_control); | 366 this_effect, this_control); |
358 for (auto i = field_type->Classes(); !i.Done(); i.Advance()) { | 367 Node* check = graph()->NewNode( |
359 Handle<Map> field_map(i.Current()); | 368 simplified()->ReferenceEqual(Type::Internal()), this_value_map, |
360 check = graph()->NewNode( | 369 jsgraph()->Constant(field_type->Classes().Current())); |
361 simplified()->ReferenceEqual(Type::Internal()), | |
362 this_value_map, jsgraph()->Constant(field_map)); | |
363 branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
364 check, this_control); | |
365 this_control = graph()->NewNode(common()->IfFalse(), branch); | |
366 this_controls.push_back( | |
367 graph()->NewNode(common()->IfTrue(), branch)); | |
368 } | |
369 exit_controls.push_back(this_control); | |
370 int const this_control_count = | |
371 static_cast<int>(this_controls.size()); | |
372 this_control = | 370 this_control = |
373 (this_control_count == 1) | 371 graph()->NewNode(common()->DeoptimizeUnless(), check, |
374 ? this_controls.front() | 372 frame_state, this_effect, this_control); |
375 : graph()->NewNode(common()->Merge(this_control_count), | 373 } else { |
376 this_control_count, | 374 DCHECK_EQ(0, field_type->NumClasses()); |
377 &this_controls.front()); | |
378 } | 375 } |
379 } else { | 376 } else { |
380 DCHECK(field_type->Is(Type::Tagged())); | 377 DCHECK(field_type->Is(Type::Tagged())); |
381 } | 378 } |
382 Handle<Map> transition_map; | 379 Handle<Map> transition_map; |
383 if (access_info.transition_map().ToHandle(&transition_map)) { | 380 if (access_info.transition_map().ToHandle(&transition_map)) { |
384 this_effect = graph()->NewNode(common()->BeginRegion(), this_effect); | 381 this_effect = graph()->NewNode(common()->BeginRegion(), this_effect); |
385 this_effect = graph()->NewNode( | 382 this_effect = graph()->NewNode( |
386 simplified()->StoreField(AccessBuilder::ForMap()), this_receiver, | 383 simplified()->StoreField(AccessBuilder::ForMap()), this_receiver, |
387 jsgraph()->Constant(transition_map), this_effect, this_control); | 384 jsgraph()->Constant(transition_map), this_effect, this_control); |
388 } | 385 } |
389 this_effect = graph()->NewNode(simplified()->StoreField(field_access), | 386 this_effect = graph()->NewNode(simplified()->StoreField(field_access), |
390 this_storage, this_value, this_effect, | 387 this_storage, this_value, this_effect, |
391 this_control); | 388 this_control); |
392 if (access_info.HasTransitionMap()) { | 389 if (access_info.HasTransitionMap()) { |
393 this_effect = | 390 this_effect = |
394 graph()->NewNode(common()->FinishRegion(), | 391 graph()->NewNode(common()->FinishRegion(), |
395 jsgraph()->UndefinedConstant(), this_effect); | 392 jsgraph()->UndefinedConstant(), this_effect); |
396 } | 393 } |
397 } | 394 } |
398 } | 395 } |
399 | 396 |
400 // Remember the final state for this property access. | 397 // Remember the final state for this property access. |
401 values.push_back(this_value); | 398 values.push_back(this_value); |
402 effects.push_back(this_effect); | 399 effects.push_back(this_effect); |
403 controls.push_back(this_control); | 400 controls.push_back(this_control); |
404 } | 401 } |
405 | 402 |
406 // Collect the fallthrough control as final "exit" control. | 403 DCHECK_NULL(fallthrough_control); |
407 if (fallthrough_control != control) { | |
408 // Mark the last fallthrough branch as deferred. | |
409 MarkAsDeferred(fallthrough_control); | |
410 } | |
411 exit_controls.push_back(fallthrough_control); | |
412 | |
413 // Also collect the "receiver is smi" control if we didn't handle the case of | |
414 // Number primitives in the polymorphic branches above. | |
415 if (receiverissmi_control != nullptr) { | |
416 // Mark the "receiver is smi" case as deferred. | |
417 MarkAsDeferred(receiverissmi_control); | |
418 DCHECK_EQ(exit_effect, receiverissmi_effect); | |
419 exit_controls.push_back(receiverissmi_control); | |
420 } | |
421 | |
422 // Generate the single "exit" point, where we get if either all map/instance | |
423 // type checks failed, or one of the assumptions inside one of the cases | |
424 // failes (i.e. failing prototype chain check). | |
425 // TODO(bmeurer): Consider falling back to IC here if deoptimization is | |
426 // disabled. | |
427 int const exit_control_count = static_cast<int>(exit_controls.size()); | |
428 Node* exit_control = | |
429 (exit_control_count == 1) | |
430 ? exit_controls.front() | |
431 : graph()->NewNode(common()->Merge(exit_control_count), | |
432 exit_control_count, &exit_controls.front()); | |
433 Node* deoptimize = | |
434 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager), | |
435 frame_state, exit_effect, exit_control); | |
436 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
437 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
438 Revisit(graph()->end()); | |
439 | 404 |
440 // Generate the final merge point for all (polymorphic) branches. | 405 // Generate the final merge point for all (polymorphic) branches. |
441 int const control_count = static_cast<int>(controls.size()); | 406 int const control_count = static_cast<int>(controls.size()); |
442 if (control_count == 0) { | 407 if (control_count == 0) { |
443 value = effect = control = jsgraph()->Dead(); | 408 value = effect = control = jsgraph()->Dead(); |
444 } else if (control_count == 1) { | 409 } else if (control_count == 1) { |
445 value = values.front(); | 410 value = values.front(); |
446 effect = effects.front(); | 411 effect = effects.front(); |
447 control = controls.front(); | 412 control = controls.front(); |
448 } else { | 413 } else { |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 | 520 |
556 // Nothing to do if we have no non-deprecated maps. | 521 // Nothing to do if we have no non-deprecated maps. |
557 if (access_infos.empty()) return NoChange(); | 522 if (access_infos.empty()) return NoChange(); |
558 | 523 |
559 // The final states for every polymorphic branch. We join them with | 524 // The final states for every polymorphic branch. We join them with |
560 // Merge+Phi+EffectPhi at the bottom. | 525 // Merge+Phi+EffectPhi at the bottom. |
561 ZoneVector<Node*> values(zone()); | 526 ZoneVector<Node*> values(zone()); |
562 ZoneVector<Node*> effects(zone()); | 527 ZoneVector<Node*> effects(zone()); |
563 ZoneVector<Node*> controls(zone()); | 528 ZoneVector<Node*> controls(zone()); |
564 | 529 |
565 // The list of "exiting" controls, which currently go to a single deoptimize. | |
566 // TODO(bmeurer): Consider using an IC as fallback. | |
567 Node* const exit_effect = effect; | |
568 ZoneVector<Node*> exit_controls(zone()); | |
569 | |
570 // Ensure that {receiver} is a heap object. | 530 // Ensure that {receiver} is a heap object. |
571 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | 531 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); |
572 Node* branch = | 532 control = graph()->NewNode(common()->DeoptimizeIf(), check, frame_state, |
573 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); | 533 effect, control); |
574 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
575 control = graph()->NewNode(common()->IfFalse(), branch); | |
576 | 534 |
577 // Load the {receiver} map. The resulting effect is the dominating effect for | 535 // Load the {receiver} map. The resulting effect is the dominating effect for |
578 // all (polymorphic) branches. | 536 // all (polymorphic) branches. |
579 Node* receiver_map = effect = | 537 Node* receiver_map = effect = |
580 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | 538 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
581 receiver, effect, control); | 539 receiver, effect, control); |
582 | 540 |
583 // Generate code for the various different element access patterns. | 541 // Generate code for the various different element access patterns. |
584 Node* fallthrough_control = control; | 542 Node* fallthrough_control = control; |
585 for (ElementAccessInfo const& access_info : access_infos) { | 543 for (size_t j = 0; j < access_infos.size(); ++j) { |
| 544 ElementAccessInfo const& access_info = access_infos[j]; |
586 Node* this_receiver = receiver; | 545 Node* this_receiver = receiver; |
587 Node* this_value = value; | 546 Node* this_value = value; |
588 Node* this_index = index; | 547 Node* this_index = index; |
589 Node* this_effect; | 548 Node* this_effect; |
590 Node* this_control; | 549 Node* this_control; |
591 | 550 |
592 // Perform map check on {receiver}. | 551 // Perform map check on {receiver}. |
593 Type* receiver_type = access_info.receiver_type(); | 552 Type* receiver_type = access_info.receiver_type(); |
594 bool receiver_is_jsarray = true; | 553 bool receiver_is_jsarray = true; |
595 { | 554 { |
596 ZoneVector<Node*> this_controls(zone()); | 555 ZoneVector<Node*> this_controls(zone()); |
597 ZoneVector<Node*> this_effects(zone()); | 556 ZoneVector<Node*> this_effects(zone()); |
| 557 size_t num_transitions = access_info.transitions().size(); |
| 558 int num_classes = access_info.receiver_type()->NumClasses(); |
598 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | 559 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); |
599 i.Advance()) { | 560 i.Advance()) { |
| 561 DCHECK_LT(0, num_classes); |
600 Handle<Map> map = i.Current(); | 562 Handle<Map> map = i.Current(); |
601 Node* check = | 563 Node* check = |
602 graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), | 564 graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), |
603 receiver_map, jsgraph()->Constant(map)); | 565 receiver_map, jsgraph()->Constant(map)); |
604 Node* branch = | 566 if (--num_classes == 0 && num_transitions == 0 && |
605 graph()->NewNode(common()->Branch(), check, fallthrough_control); | 567 j == access_infos.size() - 1) { |
606 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | 568 // Last map check on the fallthrough control path, do a conditional |
| 569 // eager deoptimization exit here. |
| 570 // TODO(turbofan): This is ugly as hell! We should probably introduce |
| 571 // macro-ish operators for property access that encapsulate this whole |
| 572 // mess. |
| 573 this_controls.push_back(graph()->NewNode(common()->DeoptimizeUnless(), |
| 574 check, frame_state, effect, |
| 575 fallthrough_control)); |
| 576 fallthrough_control = nullptr; |
| 577 } else { |
| 578 Node* branch = |
| 579 graph()->NewNode(common()->Branch(), check, fallthrough_control); |
| 580 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
| 581 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
| 582 } |
607 this_effects.push_back(effect); | 583 this_effects.push_back(effect); |
608 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
609 if (!map->IsJSArrayMap()) receiver_is_jsarray = false; | 584 if (!map->IsJSArrayMap()) receiver_is_jsarray = false; |
610 } | 585 } |
611 | 586 |
612 // Generate possible elements kind transitions. | 587 // Generate possible elements kind transitions. |
613 for (auto transition : access_info.transitions()) { | 588 for (auto transition : access_info.transitions()) { |
| 589 DCHECK_LT(0u, num_transitions); |
614 Handle<Map> transition_source = transition.first; | 590 Handle<Map> transition_source = transition.first; |
615 Handle<Map> transition_target = transition.second; | 591 Handle<Map> transition_target = transition.second; |
| 592 Node* transition_control; |
| 593 Node* transition_effect = effect; |
616 | 594 |
617 // Check if {receiver} has the specified {transition_source} map. | 595 // Check if {receiver} has the specified {transition_source} map. |
618 Node* check = graph()->NewNode( | 596 Node* check = graph()->NewNode( |
619 simplified()->ReferenceEqual(Type::Any()), receiver_map, | 597 simplified()->ReferenceEqual(Type::Any()), receiver_map, |
620 jsgraph()->HeapConstant(transition_source)); | 598 jsgraph()->HeapConstant(transition_source)); |
621 Node* branch = | 599 if (--num_transitions == 0 && j == access_infos.size() - 1) { |
622 graph()->NewNode(common()->Branch(), check, fallthrough_control); | 600 transition_control = |
| 601 graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state, |
| 602 transition_effect, fallthrough_control); |
| 603 fallthrough_control = nullptr; |
| 604 } else { |
| 605 Node* branch = |
| 606 graph()->NewNode(common()->Branch(), check, fallthrough_control); |
| 607 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
| 608 transition_control = graph()->NewNode(common()->IfTrue(), branch); |
| 609 } |
623 | 610 |
624 // Migrate {receiver} from {transition_source} to {transition_target}. | 611 // Migrate {receiver} from {transition_source} to {transition_target}. |
625 Node* transition_control = graph()->NewNode(common()->IfTrue(), branch); | |
626 Node* transition_effect = effect; | |
627 if (IsSimpleMapChangeTransition(transition_source->elements_kind(), | 612 if (IsSimpleMapChangeTransition(transition_source->elements_kind(), |
628 transition_target->elements_kind())) { | 613 transition_target->elements_kind())) { |
629 // In-place migration, just store the {transition_target} map. | 614 // In-place migration, just store the {transition_target} map. |
630 transition_effect = graph()->NewNode( | 615 transition_effect = graph()->NewNode( |
631 simplified()->StoreField(AccessBuilder::ForMap()), receiver, | 616 simplified()->StoreField(AccessBuilder::ForMap()), receiver, |
632 jsgraph()->HeapConstant(transition_target), transition_effect, | 617 jsgraph()->HeapConstant(transition_target), transition_effect, |
633 transition_control); | 618 transition_control); |
634 } else { | 619 } else { |
635 // Instance migration, let the stub deal with the {receiver}. | 620 // Instance migration, let the stub deal with the {receiver}. |
636 TransitionElementsKindStub stub(isolate(), | 621 TransitionElementsKindStub stub(isolate(), |
637 transition_source->elements_kind(), | 622 transition_source->elements_kind(), |
638 transition_target->elements_kind(), | 623 transition_target->elements_kind(), |
639 transition_source->IsJSArrayMap()); | 624 transition_source->IsJSArrayMap()); |
640 CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( | 625 CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( |
641 isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 0, | 626 isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 0, |
642 CallDescriptor::kNeedsFrameState, node->op()->properties()); | 627 CallDescriptor::kNeedsFrameState, node->op()->properties()); |
643 transition_effect = graph()->NewNode( | 628 transition_effect = graph()->NewNode( |
644 common()->Call(desc), jsgraph()->HeapConstant(stub.GetCode()), | 629 common()->Call(desc), jsgraph()->HeapConstant(stub.GetCode()), |
645 receiver, jsgraph()->HeapConstant(transition_target), context, | 630 receiver, jsgraph()->HeapConstant(transition_target), context, |
646 frame_state, transition_effect, transition_control); | 631 frame_state, transition_effect, transition_control); |
647 } | 632 } |
648 this_controls.push_back(transition_control); | 633 this_controls.push_back(transition_control); |
649 this_effects.push_back(transition_effect); | 634 this_effects.push_back(transition_effect); |
650 | |
651 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
652 } | 635 } |
653 | 636 |
654 // Create single chokepoint for the control. | 637 // Create single chokepoint for the control. |
655 int const this_control_count = static_cast<int>(this_controls.size()); | 638 int const this_control_count = static_cast<int>(this_controls.size()); |
656 if (this_control_count == 1) { | 639 if (this_control_count == 1) { |
657 this_control = this_controls.front(); | 640 this_control = this_controls.front(); |
658 this_effect = this_effects.front(); | 641 this_effect = this_effects.front(); |
659 } else { | 642 } else { |
660 this_control = | 643 this_control = |
661 graph()->NewNode(common()->Merge(this_control_count), | 644 graph()->NewNode(common()->Merge(this_control_count), |
(...skipping 10 matching lines...) Expand all Loading... |
672 // not compatible with (monomorphic) keyed stores. | 655 // not compatible with (monomorphic) keyed stores. |
673 Handle<JSObject> holder; | 656 Handle<JSObject> holder; |
674 if (access_info.holder().ToHandle(&holder)) { | 657 if (access_info.holder().ToHandle(&holder)) { |
675 AssumePrototypesStable(receiver_type, native_context, holder); | 658 AssumePrototypesStable(receiver_type, native_context, holder); |
676 } | 659 } |
677 | 660 |
678 // Check that the {index} is actually a Number. | 661 // Check that the {index} is actually a Number. |
679 if (!NumberMatcher(this_index).HasValue()) { | 662 if (!NumberMatcher(this_index).HasValue()) { |
680 Node* check = | 663 Node* check = |
681 graph()->NewNode(simplified()->ObjectIsNumber(), this_index); | 664 graph()->NewNode(simplified()->ObjectIsNumber(), this_index); |
682 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 665 this_control = graph()->NewNode(common()->DeoptimizeUnless(), check, |
683 check, this_control); | 666 frame_state, this_effect, this_control); |
684 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
685 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
686 this_index = graph()->NewNode(common()->Guard(Type::Number()), this_index, | 667 this_index = graph()->NewNode(common()->Guard(Type::Number()), this_index, |
687 this_control); | 668 this_control); |
688 } | 669 } |
689 | 670 |
690 // Convert the {index} to an unsigned32 value and check if the result is | 671 // Convert the {index} to an unsigned32 value and check if the result is |
691 // equal to the original {index}. | 672 // equal to the original {index}. |
692 if (!NumberMatcher(this_index).IsInRange(0.0, kMaxUInt32)) { | 673 if (!NumberMatcher(this_index).IsInRange(0.0, kMaxUInt32)) { |
693 Node* this_index32 = | 674 Node* this_index32 = |
694 graph()->NewNode(simplified()->NumberToUint32(), this_index); | 675 graph()->NewNode(simplified()->NumberToUint32(), this_index); |
695 Node* check = graph()->NewNode(simplified()->NumberEqual(), this_index32, | 676 Node* check = graph()->NewNode(simplified()->NumberEqual(), this_index32, |
696 this_index); | 677 this_index); |
697 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 678 this_control = graph()->NewNode(common()->DeoptimizeUnless(), check, |
698 check, this_control); | 679 frame_state, this_effect, this_control); |
699 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
700 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
701 this_index = this_index32; | 680 this_index = this_index32; |
702 } | 681 } |
703 | 682 |
704 // TODO(bmeurer): We currently specialize based on elements kind. We should | 683 // TODO(bmeurer): We currently specialize based on elements kind. We should |
705 // also be able to properly support strings and other JSObjects here. | 684 // also be able to properly support strings and other JSObjects here. |
706 ElementsKind elements_kind = access_info.elements_kind(); | 685 ElementsKind elements_kind = access_info.elements_kind(); |
707 | 686 |
708 // Load the elements for the {receiver}. | 687 // Load the elements for the {receiver}. |
709 Node* this_elements = this_effect = graph()->NewNode( | 688 Node* this_elements = this_effect = graph()->NewNode( |
710 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), | 689 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), |
711 this_receiver, this_effect, this_control); | 690 this_receiver, this_effect, this_control); |
712 | 691 |
713 // Don't try to store to a copy-on-write backing store. | 692 // Don't try to store to a copy-on-write backing store. |
714 if (access_mode == AccessMode::kStore && | 693 if (access_mode == AccessMode::kStore && |
715 IsFastSmiOrObjectElementsKind(elements_kind)) { | 694 IsFastSmiOrObjectElementsKind(elements_kind)) { |
716 Node* this_elements_map = this_effect = | 695 Node* this_elements_map = this_effect = |
717 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | 696 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
718 this_elements, this_effect, this_control); | 697 this_elements, this_effect, this_control); |
719 check = graph()->NewNode( | 698 Node* check = graph()->NewNode( |
720 simplified()->ReferenceEqual(Type::Any()), this_elements_map, | 699 simplified()->ReferenceEqual(Type::Any()), this_elements_map, |
721 jsgraph()->HeapConstant(factory()->fixed_array_map())); | 700 jsgraph()->HeapConstant(factory()->fixed_array_map())); |
722 branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, | 701 this_control = graph()->NewNode(common()->DeoptimizeUnless(), check, |
723 this_control); | 702 frame_state, this_effect, this_control); |
724 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
725 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
726 } | 703 } |
727 | 704 |
728 // Load the length of the {receiver}. | 705 // Load the length of the {receiver}. |
729 Node* this_length = this_effect = | 706 Node* this_length = this_effect = |
730 receiver_is_jsarray | 707 receiver_is_jsarray |
731 ? graph()->NewNode( | 708 ? graph()->NewNode( |
732 simplified()->LoadField( | 709 simplified()->LoadField( |
733 AccessBuilder::ForJSArrayLength(elements_kind)), | 710 AccessBuilder::ForJSArrayLength(elements_kind)), |
734 this_receiver, this_effect, this_control) | 711 this_receiver, this_effect, this_control) |
735 : graph()->NewNode( | 712 : graph()->NewNode( |
736 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), | 713 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), |
737 this_elements, this_effect, this_control); | 714 this_elements, this_effect, this_control); |
738 | 715 |
739 // Check that the {index} is in the valid range for the {receiver}. | 716 // Check that the {index} is in the valid range for the {receiver}. |
740 Node* check = graph()->NewNode(simplified()->NumberLessThan(), this_index, | 717 Node* check = graph()->NewNode(simplified()->NumberLessThan(), this_index, |
741 this_length); | 718 this_length); |
742 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, | 719 this_control = graph()->NewNode(common()->DeoptimizeUnless(), check, |
743 this_control); | 720 frame_state, this_effect, this_control); |
744 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
745 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
746 | 721 |
747 // Compute the element access. | 722 // Compute the element access. |
748 Type* element_type = Type::Any(); | 723 Type* element_type = Type::Any(); |
749 MachineType element_machine_type = MachineType::AnyTagged(); | 724 MachineType element_machine_type = MachineType::AnyTagged(); |
750 if (IsFastDoubleElementsKind(elements_kind)) { | 725 if (IsFastDoubleElementsKind(elements_kind)) { |
751 element_type = Type::Number(); | 726 element_type = Type::Number(); |
752 element_machine_type = MachineType::Float64(); | 727 element_machine_type = MachineType::Float64(); |
753 } else if (IsFastSmiElementsKind(elements_kind)) { | 728 } else if (IsFastSmiElementsKind(elements_kind)) { |
754 element_type = type_cache_.kSmi; | 729 element_type = type_cache_.kSmi; |
755 } | 730 } |
(...skipping 18 matching lines...) Expand all Loading... |
774 simplified()->LoadElement(element_access), this_elements, this_index, | 749 simplified()->LoadElement(element_access), this_elements, this_index, |
775 this_effect, this_control); | 750 this_effect, this_control); |
776 // Handle loading from holey backing stores correctly, by either mapping | 751 // Handle loading from holey backing stores correctly, by either mapping |
777 // the hole to undefined if possible, or deoptimizing otherwise. | 752 // the hole to undefined if possible, or deoptimizing otherwise. |
778 if (elements_kind == FAST_HOLEY_ELEMENTS || | 753 if (elements_kind == FAST_HOLEY_ELEMENTS || |
779 elements_kind == FAST_HOLEY_SMI_ELEMENTS) { | 754 elements_kind == FAST_HOLEY_SMI_ELEMENTS) { |
780 // Perform the hole check on the result. | 755 // Perform the hole check on the result. |
781 Node* check = | 756 Node* check = |
782 graph()->NewNode(simplified()->ReferenceEqual(element_access.type), | 757 graph()->NewNode(simplified()->ReferenceEqual(element_access.type), |
783 this_value, jsgraph()->TheHoleConstant()); | 758 this_value, jsgraph()->TheHoleConstant()); |
784 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), | |
785 check, this_control); | |
786 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | |
787 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | |
788 // Check if we are allowed to turn the hole into undefined. | 759 // Check if we are allowed to turn the hole into undefined. |
789 Type* initial_holey_array_type = Type::Class( | 760 Type* initial_holey_array_type = Type::Class( |
790 handle(isolate()->get_initial_js_array_map(elements_kind)), | 761 handle(isolate()->get_initial_js_array_map(elements_kind)), |
791 graph()->zone()); | 762 graph()->zone()); |
792 if (receiver_type->NowIs(initial_holey_array_type) && | 763 if (receiver_type->NowIs(initial_holey_array_type) && |
793 isolate()->IsFastArrayConstructorPrototypeChainIntact()) { | 764 isolate()->IsFastArrayConstructorPrototypeChainIntact()) { |
| 765 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| 766 check, this_control); |
| 767 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
| 768 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
794 // Add a code dependency on the array protector cell. | 769 // Add a code dependency on the array protector cell. |
795 AssumePrototypesStable(receiver_type, native_context, | 770 AssumePrototypesStable(receiver_type, native_context, |
796 isolate()->initial_object_prototype()); | 771 isolate()->initial_object_prototype()); |
797 dependencies()->AssumePropertyCell(factory()->array_protector()); | 772 dependencies()->AssumePropertyCell(factory()->array_protector()); |
798 // Turn the hole into undefined. | 773 // Turn the hole into undefined. |
799 this_control = | 774 this_control = |
800 graph()->NewNode(common()->Merge(2), if_true, if_false); | 775 graph()->NewNode(common()->Merge(2), if_true, if_false); |
801 this_value = graph()->NewNode( | 776 this_value = graph()->NewNode( |
802 common()->Phi(MachineRepresentation::kTagged, 2), | 777 common()->Phi(MachineRepresentation::kTagged, 2), |
803 jsgraph()->UndefinedConstant(), this_value, this_control); | 778 jsgraph()->UndefinedConstant(), this_value, this_control); |
804 element_type = | 779 element_type = |
805 Type::Union(element_type, Type::Undefined(), graph()->zone()); | 780 Type::Union(element_type, Type::Undefined(), graph()->zone()); |
806 } else { | 781 } else { |
807 // Deoptimize in case of the hole. | 782 // Deoptimize in case of the hole. |
808 exit_controls.push_back(if_true); | 783 this_control = |
809 this_control = if_false; | 784 graph()->NewNode(common()->DeoptimizeIf(), check, frame_state, |
| 785 this_effect, this_control); |
810 } | 786 } |
811 // Rename the result to represent the actual type (not polluted by the | 787 // Rename the result to represent the actual type (not polluted by the |
812 // hole). | 788 // hole). |
813 this_value = graph()->NewNode(common()->Guard(element_type), this_value, | 789 this_value = graph()->NewNode(common()->Guard(element_type), this_value, |
814 this_control); | 790 this_control); |
815 } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) { | 791 } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) { |
816 // Perform the hole check on the result. | 792 // Perform the hole check on the result. |
817 Node* check = | 793 Node* check = |
818 graph()->NewNode(simplified()->NumberIsHoleNaN(), this_value); | 794 graph()->NewNode(simplified()->NumberIsHoleNaN(), this_value); |
819 // Check if we are allowed to return the hole directly. | 795 // Check if we are allowed to return the hole directly. |
820 Type* initial_holey_array_type = Type::Class( | 796 Type* initial_holey_array_type = Type::Class( |
821 handle(isolate()->get_initial_js_array_map(elements_kind)), | 797 handle(isolate()->get_initial_js_array_map(elements_kind)), |
822 graph()->zone()); | 798 graph()->zone()); |
823 if (receiver_type->NowIs(initial_holey_array_type) && | 799 if (receiver_type->NowIs(initial_holey_array_type) && |
824 isolate()->IsFastArrayConstructorPrototypeChainIntact()) { | 800 isolate()->IsFastArrayConstructorPrototypeChainIntact()) { |
825 // Add a code dependency on the array protector cell. | 801 // Add a code dependency on the array protector cell. |
826 AssumePrototypesStable(receiver_type, native_context, | 802 AssumePrototypesStable(receiver_type, native_context, |
827 isolate()->initial_object_prototype()); | 803 isolate()->initial_object_prototype()); |
828 dependencies()->AssumePropertyCell(factory()->array_protector()); | 804 dependencies()->AssumePropertyCell(factory()->array_protector()); |
829 // Turn the hole into undefined. | 805 // Turn the hole into undefined. |
830 this_value = graph()->NewNode( | 806 this_value = graph()->NewNode( |
831 common()->Select(MachineRepresentation::kTagged, | 807 common()->Select(MachineRepresentation::kTagged, |
832 BranchHint::kFalse), | 808 BranchHint::kFalse), |
833 check, jsgraph()->UndefinedConstant(), this_value); | 809 check, jsgraph()->UndefinedConstant(), this_value); |
834 } else { | 810 } else { |
835 // Deoptimize in case of the hole. | 811 // Deoptimize in case of the hole. |
836 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), | 812 this_control = |
837 check, this_control); | 813 graph()->NewNode(common()->DeoptimizeIf(), check, frame_state, |
838 this_control = graph()->NewNode(common()->IfFalse(), branch); | 814 this_effect, this_control); |
839 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
840 } | 815 } |
841 } | 816 } |
842 } else { | 817 } else { |
843 DCHECK_EQ(AccessMode::kStore, access_mode); | 818 DCHECK_EQ(AccessMode::kStore, access_mode); |
844 if (IsFastSmiElementsKind(elements_kind)) { | 819 if (IsFastSmiElementsKind(elements_kind)) { |
845 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), this_value); | 820 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), this_value); |
846 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 821 this_control = graph()->NewNode(common()->DeoptimizeUnless(), check, |
847 check, this_control); | 822 frame_state, this_effect, this_control); |
848 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
849 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
850 this_value = graph()->NewNode(common()->Guard(type_cache_.kSmi), | 823 this_value = graph()->NewNode(common()->Guard(type_cache_.kSmi), |
851 this_value, this_control); | 824 this_value, this_control); |
852 } else if (IsFastDoubleElementsKind(elements_kind)) { | 825 } else if (IsFastDoubleElementsKind(elements_kind)) { |
853 Node* check = | 826 Node* check = |
854 graph()->NewNode(simplified()->ObjectIsNumber(), this_value); | 827 graph()->NewNode(simplified()->ObjectIsNumber(), this_value); |
855 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 828 this_control = graph()->NewNode(common()->DeoptimizeUnless(), check, |
856 check, this_control); | 829 frame_state, this_effect, this_control); |
857 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
858 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
859 this_value = graph()->NewNode(common()->Guard(Type::Number()), | 830 this_value = graph()->NewNode(common()->Guard(Type::Number()), |
860 this_value, this_control); | 831 this_value, this_control); |
861 } | 832 } |
862 this_effect = graph()->NewNode(simplified()->StoreElement(element_access), | 833 this_effect = graph()->NewNode(simplified()->StoreElement(element_access), |
863 this_elements, this_index, this_value, | 834 this_elements, this_index, this_value, |
864 this_effect, this_control); | 835 this_effect, this_control); |
865 } | 836 } |
866 | 837 |
867 // Remember the final state for this element access. | 838 // Remember the final state for this element access. |
868 values.push_back(this_value); | 839 values.push_back(this_value); |
869 effects.push_back(this_effect); | 840 effects.push_back(this_effect); |
870 controls.push_back(this_control); | 841 controls.push_back(this_control); |
871 } | 842 } |
872 | 843 |
873 // Collect the fallthrough control as final "exit" control. | 844 DCHECK_NULL(fallthrough_control); |
874 if (fallthrough_control != control) { | |
875 // Mark the last fallthrough branch as deferred. | |
876 MarkAsDeferred(fallthrough_control); | |
877 } | |
878 exit_controls.push_back(fallthrough_control); | |
879 | |
880 // Generate the single "exit" point, where we get if either all map/instance | |
881 // type checks failed, or one of the assumptions inside one of the cases | |
882 // failes (i.e. failing prototype chain check). | |
883 // TODO(bmeurer): Consider falling back to IC here if deoptimization is | |
884 // disabled. | |
885 int const exit_control_count = static_cast<int>(exit_controls.size()); | |
886 Node* exit_control = | |
887 (exit_control_count == 1) | |
888 ? exit_controls.front() | |
889 : graph()->NewNode(common()->Merge(exit_control_count), | |
890 exit_control_count, &exit_controls.front()); | |
891 Node* deoptimize = | |
892 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager), | |
893 frame_state, exit_effect, exit_control); | |
894 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
895 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
896 Revisit(graph()->end()); | |
897 | 845 |
898 // Generate the final merge point for all (polymorphic) branches. | 846 // Generate the final merge point for all (polymorphic) branches. |
899 int const control_count = static_cast<int>(controls.size()); | 847 int const control_count = static_cast<int>(controls.size()); |
900 if (control_count == 0) { | 848 if (control_count == 0) { |
901 value = effect = control = jsgraph()->Dead(); | 849 value = effect = control = jsgraph()->Dead(); |
902 } else if (control_count == 1) { | 850 } else if (control_count == 1) { |
903 value = values.front(); | 851 value = values.front(); |
904 effect = effects.front(); | 852 effect = effects.front(); |
905 control = controls.front(); | 853 control = controls.front(); |
906 } else { | 854 } else { |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1041 Handle<JSFunction> constructor; | 989 Handle<JSFunction> constructor; |
1042 if (Map::GetConstructorFunction(map, native_context) | 990 if (Map::GetConstructorFunction(map, native_context) |
1043 .ToHandle(&constructor)) { | 991 .ToHandle(&constructor)) { |
1044 map = handle(constructor->initial_map(), isolate()); | 992 map = handle(constructor->initial_map(), isolate()); |
1045 } | 993 } |
1046 dependencies()->AssumePrototypeMapsStable(map, holder); | 994 dependencies()->AssumePrototypeMapsStable(map, holder); |
1047 } | 995 } |
1048 } | 996 } |
1049 | 997 |
1050 | 998 |
1051 void JSNativeContextSpecialization::MarkAsDeferred(Node* if_projection) { | |
1052 Node* branch = NodeProperties::GetControlInput(if_projection); | |
1053 DCHECK_EQ(IrOpcode::kBranch, branch->opcode()); | |
1054 if (if_projection->opcode() == IrOpcode::kIfTrue) { | |
1055 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kFalse)); | |
1056 } else { | |
1057 DCHECK_EQ(IrOpcode::kIfFalse, if_projection->opcode()); | |
1058 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kTrue)); | |
1059 } | |
1060 } | |
1061 | |
1062 | |
1063 MaybeHandle<Context> JSNativeContextSpecialization::GetNativeContext( | 999 MaybeHandle<Context> JSNativeContextSpecialization::GetNativeContext( |
1064 Node* node) { | 1000 Node* node) { |
1065 Node* const context = NodeProperties::GetContextInput(node); | 1001 Node* const context = NodeProperties::GetContextInput(node); |
1066 return NodeProperties::GetSpecializationNativeContext(context, | 1002 return NodeProperties::GetSpecializationNativeContext(context, |
1067 native_context()); | 1003 native_context()); |
1068 } | 1004 } |
1069 | 1005 |
1070 | 1006 |
1071 Graph* JSNativeContextSpecialization::graph() const { | 1007 Graph* JSNativeContextSpecialization::graph() const { |
1072 return jsgraph()->graph(); | 1008 return jsgraph()->graph(); |
(...skipping 25 matching lines...) Expand all Loading... |
1098 } | 1034 } |
1099 | 1035 |
1100 | 1036 |
1101 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 1037 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
1102 return jsgraph()->simplified(); | 1038 return jsgraph()->simplified(); |
1103 } | 1039 } |
1104 | 1040 |
1105 } // namespace compiler | 1041 } // namespace compiler |
1106 } // namespace internal | 1042 } // namespace internal |
1107 } // namespace v8 | 1043 } // namespace v8 |
OLD | NEW |