| 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 |