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