| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "src/compiler/js-global-specialization.h" | |
| 6 | |
| 7 #include "src/compilation-dependencies.h" | |
| 8 #include "src/compiler/access-builder.h" | |
| 9 #include "src/compiler/js-graph.h" | |
| 10 #include "src/compiler/js-operator.h" | |
| 11 #include "src/contexts.h" | |
| 12 #include "src/field-index-inl.h" | |
| 13 #include "src/lookup.h" | |
| 14 #include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker! | |
| 15 #include "src/type-feedback-vector.h" | |
| 16 | |
| 17 namespace v8 { | |
| 18 namespace internal { | |
| 19 namespace compiler { | |
| 20 | |
| 21 struct JSGlobalSpecialization::ScriptContextTableLookupResult { | |
| 22 Handle<Context> context; | |
| 23 bool immutable; | |
| 24 int index; | |
| 25 }; | |
| 26 | |
| 27 | |
| 28 JSGlobalSpecialization::JSGlobalSpecialization( | |
| 29 Editor* editor, JSGraph* jsgraph, Flags flags, | |
| 30 Handle<GlobalObject> global_object, CompilationDependencies* dependencies, | |
| 31 Zone* zone) | |
| 32 : AdvancedReducer(editor), | |
| 33 jsgraph_(jsgraph), | |
| 34 flags_(flags), | |
| 35 global_object_(global_object), | |
| 36 dependencies_(dependencies), | |
| 37 zone_(zone) {} | |
| 38 | |
| 39 | |
| 40 Reduction JSGlobalSpecialization::Reduce(Node* node) { | |
| 41 switch (node->opcode()) { | |
| 42 case IrOpcode::kJSLoadGlobal: | |
| 43 return ReduceJSLoadGlobal(node); | |
| 44 case IrOpcode::kJSStoreGlobal: | |
| 45 return ReduceJSStoreGlobal(node); | |
| 46 case IrOpcode::kJSLoadNamed: | |
| 47 return ReduceJSLoadNamed(node); | |
| 48 default: | |
| 49 break; | |
| 50 } | |
| 51 return NoChange(); | |
| 52 } | |
| 53 | |
| 54 | |
| 55 Reduction JSGlobalSpecialization::ReduceJSLoadGlobal(Node* node) { | |
| 56 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); | |
| 57 Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); | |
| 58 Node* effect = NodeProperties::GetEffectInput(node); | |
| 59 Node* control = NodeProperties::GetControlInput(node); | |
| 60 | |
| 61 // Try to lookup the name on the script context table first (lexical scoping). | |
| 62 ScriptContextTableLookupResult result; | |
| 63 if (LookupInScriptContextTable(name, &result)) { | |
| 64 Node* context = jsgraph()->Constant(result.context); | |
| 65 Node* value = effect = graph()->NewNode( | |
| 66 javascript()->LoadContext(0, result.index, result.immutable), context, | |
| 67 context, effect); | |
| 68 return Replace(node, value, effect); | |
| 69 } | |
| 70 | |
| 71 // Lookup on the global object instead. We only deal with own data | |
| 72 // properties of the global object here (represented as PropertyCell). | |
| 73 LookupIterator it(global_object(), name, LookupIterator::OWN); | |
| 74 if (it.state() != LookupIterator::DATA) return NoChange(); | |
| 75 Handle<PropertyCell> property_cell = it.GetPropertyCell(); | |
| 76 PropertyDetails property_details = property_cell->property_details(); | |
| 77 Handle<Object> property_cell_value(property_cell->value(), isolate()); | |
| 78 | |
| 79 // Load from non-configurable, read-only data property on the global | |
| 80 // object can be constant-folded, even without deoptimization support. | |
| 81 if (!property_details.IsConfigurable() && property_details.IsReadOnly()) { | |
| 82 return Replace(node, property_cell_value); | |
| 83 } | |
| 84 | |
| 85 // Load from constant/undefined global property can be constant-folded | |
| 86 // with deoptimization support, by adding a code dependency on the cell. | |
| 87 if ((property_details.cell_type() == PropertyCellType::kConstant || | |
| 88 property_details.cell_type() == PropertyCellType::kUndefined) && | |
| 89 (flags() & kDeoptimizationEnabled)) { | |
| 90 dependencies()->AssumePropertyCell(property_cell); | |
| 91 return Replace(node, property_cell_value); | |
| 92 } | |
| 93 | |
| 94 // Load from constant type global property can benefit from representation | |
| 95 // (and map) feedback with deoptimization support (requires code dependency). | |
| 96 if (property_details.cell_type() == PropertyCellType::kConstantType && | |
| 97 (flags() & kDeoptimizationEnabled)) { | |
| 98 dependencies()->AssumePropertyCell(property_cell); | |
| 99 // Compute proper type based on the current value in the cell. | |
| 100 Type* property_cell_value_type; | |
| 101 if (property_cell_value->IsSmi()) { | |
| 102 property_cell_value_type = Type::Intersect( | |
| 103 Type::SignedSmall(), Type::TaggedSigned(), graph()->zone()); | |
| 104 } else if (property_cell_value->IsNumber()) { | |
| 105 property_cell_value_type = Type::Intersect( | |
| 106 Type::Number(), Type::TaggedPointer(), graph()->zone()); | |
| 107 } else { | |
| 108 property_cell_value_type = Type::Of(property_cell_value, graph()->zone()); | |
| 109 } | |
| 110 Node* value = effect = graph()->NewNode( | |
| 111 simplified()->LoadField( | |
| 112 AccessBuilder::ForPropertyCellValue(property_cell_value_type)), | |
| 113 jsgraph()->Constant(property_cell), effect, control); | |
| 114 return Replace(node, value, effect); | |
| 115 } | |
| 116 | |
| 117 // Load from non-configurable, data property on the global can be lowered to | |
| 118 // a field load, even without deoptimization, because the property cannot be | |
| 119 // deleted or reconfigured to an accessor/interceptor property. | |
| 120 if (property_details.IsConfigurable()) { | |
| 121 // With deoptimization support, we can lower loads even from configurable | |
| 122 // data properties on the global object, by adding a code dependency on | |
| 123 // the cell. | |
| 124 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | |
| 125 dependencies()->AssumePropertyCell(property_cell); | |
| 126 } | |
| 127 Node* value = effect = graph()->NewNode( | |
| 128 simplified()->LoadField(AccessBuilder::ForPropertyCellValue()), | |
| 129 jsgraph()->Constant(property_cell), effect, control); | |
| 130 return Replace(node, value, effect); | |
| 131 } | |
| 132 | |
| 133 | |
| 134 Reduction JSGlobalSpecialization::ReduceJSStoreGlobal(Node* node) { | |
| 135 DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode()); | |
| 136 Handle<Name> name = StoreGlobalParametersOf(node->op()).name(); | |
| 137 Node* value = NodeProperties::GetValueInput(node, 2); | |
| 138 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | |
| 139 Node* effect = NodeProperties::GetEffectInput(node); | |
| 140 Node* control = NodeProperties::GetControlInput(node); | |
| 141 | |
| 142 // Try to lookup the name on the script context table first (lexical scoping). | |
| 143 ScriptContextTableLookupResult result; | |
| 144 if (LookupInScriptContextTable(name, &result)) { | |
| 145 if (result.immutable) return NoChange(); | |
| 146 Node* context = jsgraph()->Constant(result.context); | |
| 147 effect = graph()->NewNode(javascript()->StoreContext(0, result.index), | |
| 148 context, value, context, effect, control); | |
| 149 return Replace(node, value, effect, control); | |
| 150 } | |
| 151 | |
| 152 // Lookup on the global object instead. We only deal with own data | |
| 153 // properties of the global object here (represented as PropertyCell). | |
| 154 LookupIterator it(global_object(), name, LookupIterator::OWN); | |
| 155 if (it.state() != LookupIterator::DATA) return NoChange(); | |
| 156 Handle<PropertyCell> property_cell = it.GetPropertyCell(); | |
| 157 PropertyDetails property_details = property_cell->property_details(); | |
| 158 Handle<Object> property_cell_value(property_cell->value(), isolate()); | |
| 159 | |
| 160 // Don't even bother trying to lower stores to read-only data properties. | |
| 161 if (property_details.IsReadOnly()) return NoChange(); | |
| 162 switch (property_details.cell_type()) { | |
| 163 case PropertyCellType::kUndefined: { | |
| 164 return NoChange(); | |
| 165 } | |
| 166 case PropertyCellType::kConstant: { | |
| 167 // Store to constant property cell requires deoptimization support, | |
| 168 // because we might even need to eager deoptimize for mismatch. | |
| 169 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | |
| 170 dependencies()->AssumePropertyCell(property_cell); | |
| 171 Node* check = | |
| 172 graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value, | |
| 173 jsgraph()->Constant(property_cell_value)); | |
| 174 Node* branch = | |
| 175 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); | |
| 176 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | |
| 177 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
| 178 effect, if_false); | |
| 179 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
| 180 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
| 181 control = graph()->NewNode(common()->IfTrue(), branch); | |
| 182 return Replace(node, value, effect, control); | |
| 183 } | |
| 184 case PropertyCellType::kConstantType: { | |
| 185 // Store to constant-type property cell requires deoptimization support, | |
| 186 // because we might even need to eager deoptimize for mismatch. | |
| 187 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | |
| 188 dependencies()->AssumePropertyCell(property_cell); | |
| 189 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); | |
| 190 if (property_cell_value->IsHeapObject()) { | |
| 191 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), | |
| 192 check, control); | |
| 193 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | |
| 194 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
| 195 effect, if_true); | |
| 196 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
| 197 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
| 198 control = graph()->NewNode(common()->IfFalse(), branch); | |
| 199 Node* value_map = | |
| 200 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
| 201 value, effect, control); | |
| 202 Handle<Map> property_cell_value_map( | |
| 203 Handle<HeapObject>::cast(property_cell_value)->map(), isolate()); | |
| 204 check = graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
| 205 value_map, | |
| 206 jsgraph()->Constant(property_cell_value_map)); | |
| 207 } | |
| 208 Node* branch = | |
| 209 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); | |
| 210 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | |
| 211 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
| 212 effect, if_false); | |
| 213 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
| 214 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
| 215 control = graph()->NewNode(common()->IfTrue(), branch); | |
| 216 break; | |
| 217 } | |
| 218 case PropertyCellType::kMutable: { | |
| 219 // Store to non-configurable, data property on the global can be lowered | |
| 220 // to a field store, even without deoptimization, because the property | |
| 221 // cannot be deleted or reconfigured to an accessor/interceptor property. | |
| 222 if (property_details.IsConfigurable()) { | |
| 223 // With deoptimization support, we can lower stores even to configurable | |
| 224 // data properties on the global object, by adding a code dependency on | |
| 225 // the cell. | |
| 226 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | |
| 227 dependencies()->AssumePropertyCell(property_cell); | |
| 228 } | |
| 229 break; | |
| 230 } | |
| 231 } | |
| 232 effect = graph()->NewNode( | |
| 233 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), | |
| 234 jsgraph()->Constant(property_cell), value, effect, control); | |
| 235 return Replace(node, value, effect, control); | |
| 236 } | |
| 237 | |
| 238 | |
| 239 // This class encapsulates all information required to access a certain | |
| 240 // object property, either on the object itself or on the prototype chain. | |
| 241 class JSGlobalSpecialization::PropertyAccessInfo final { | |
| 242 public: | |
| 243 enum Kind { kInvalid, kData, kDataConstant }; | |
| 244 | |
| 245 static PropertyAccessInfo DataConstant(Type* receiver_type, | |
| 246 Handle<Object> constant, | |
| 247 MaybeHandle<JSObject> holder) { | |
| 248 return PropertyAccessInfo(holder, constant, receiver_type); | |
| 249 } | |
| 250 static PropertyAccessInfo Data(Type* receiver_type, FieldIndex field_index, | |
| 251 Representation field_representation, | |
| 252 MaybeHandle<JSObject> holder) { | |
| 253 return PropertyAccessInfo(holder, field_index, field_representation, | |
| 254 receiver_type); | |
| 255 } | |
| 256 | |
| 257 PropertyAccessInfo() : kind_(kInvalid) {} | |
| 258 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant, | |
| 259 Type* receiver_type) | |
| 260 : kind_(kDataConstant), | |
| 261 receiver_type_(receiver_type), | |
| 262 constant_(constant), | |
| 263 holder_(holder) {} | |
| 264 PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index, | |
| 265 Representation field_representation, Type* receiver_type) | |
| 266 : kind_(kData), | |
| 267 receiver_type_(receiver_type), | |
| 268 holder_(holder), | |
| 269 field_index_(field_index), | |
| 270 field_representation_(field_representation) {} | |
| 271 | |
| 272 bool IsDataConstant() const { return kind() == kDataConstant; } | |
| 273 bool IsData() const { return kind() == kData; } | |
| 274 | |
| 275 Kind kind() const { return kind_; } | |
| 276 MaybeHandle<JSObject> holder() const { return holder_; } | |
| 277 Handle<Object> constant() const { return constant_; } | |
| 278 FieldIndex field_index() const { return field_index_; } | |
| 279 Representation field_representation() const { return field_representation_; } | |
| 280 Type* receiver_type() const { return receiver_type_; } | |
| 281 | |
| 282 private: | |
| 283 Kind kind_; | |
| 284 Type* receiver_type_; | |
| 285 Handle<Object> constant_; | |
| 286 MaybeHandle<JSObject> holder_; | |
| 287 FieldIndex field_index_; | |
| 288 Representation field_representation_; | |
| 289 }; | |
| 290 | |
| 291 | |
| 292 namespace { | |
| 293 | |
| 294 bool CanInlinePropertyAccess(Handle<Map> map) { | |
| 295 // TODO(bmeurer): Do something about the number stuff. | |
| 296 if (map->instance_type() == HEAP_NUMBER_TYPE) return false; | |
| 297 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; | |
| 298 return map->IsJSObjectMap() && !map->is_dictionary_map() && | |
| 299 !map->has_named_interceptor() && | |
| 300 // TODO(verwaest): Whitelist contexts to which we have access. | |
| 301 !map->is_access_check_needed(); | |
| 302 } | |
| 303 | |
| 304 } // namespace | |
| 305 | |
| 306 | |
| 307 bool JSGlobalSpecialization::ComputePropertyAccessInfo( | |
| 308 Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) { | |
| 309 MaybeHandle<JSObject> holder; | |
| 310 Type* receiver_type = Type::Class(map, graph()->zone()); | |
| 311 while (CanInlinePropertyAccess(map)) { | |
| 312 // Lookup the named property on the {map}. | |
| 313 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); | |
| 314 int const number = descriptors->SearchWithCache(*name, *map); | |
| 315 if (number != DescriptorArray::kNotFound) { | |
| 316 PropertyDetails const details = descriptors->GetDetails(number); | |
| 317 if (details.type() == DATA_CONSTANT) { | |
| 318 *access_info = PropertyAccessInfo::DataConstant( | |
| 319 receiver_type, handle(descriptors->GetValue(number), isolate()), | |
| 320 holder); | |
| 321 return true; | |
| 322 } else if (details.type() == DATA) { | |
| 323 int index = descriptors->GetFieldIndex(number); | |
| 324 Representation field_representation = details.representation(); | |
| 325 FieldIndex field_index = FieldIndex::ForPropertyIndex( | |
| 326 *map, index, field_representation.IsDouble()); | |
| 327 *access_info = PropertyAccessInfo::Data(receiver_type, field_index, | |
| 328 field_representation, holder); | |
| 329 return true; | |
| 330 } else { | |
| 331 // TODO(bmeurer): Add support for accessors. | |
| 332 break; | |
| 333 } | |
| 334 } | |
| 335 | |
| 336 // Don't search on the prototype chain for special indices in case of | |
| 337 // integer indexed exotic objects (see ES6 section 9.4.5). | |
| 338 if (map->IsJSTypedArrayMap() && name->IsString() && | |
| 339 IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) { | |
| 340 break; | |
| 341 } | |
| 342 | |
| 343 // Walk up the prototype chain. | |
| 344 if (!map->prototype()->IsJSObject()) { | |
| 345 // TODO(bmeurer): Handle the not found case if the prototype is null. | |
| 346 break; | |
| 347 } | |
| 348 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate()); | |
| 349 if (map_prototype->map()->is_deprecated()) { | |
| 350 // Try to migrate the prototype object so we don't embed the deprecated | |
| 351 // map into the optimized code. | |
| 352 JSObject::TryMigrateInstance(map_prototype); | |
| 353 } | |
| 354 map = handle(map_prototype->map(), isolate()); | |
| 355 holder = map_prototype; | |
| 356 } | |
| 357 return false; | |
| 358 } | |
| 359 | |
| 360 | |
| 361 bool JSGlobalSpecialization::ComputePropertyAccessInfos( | |
| 362 MapHandleList const& maps, Handle<Name> name, | |
| 363 ZoneVector<PropertyAccessInfo>* access_infos) { | |
| 364 for (Handle<Map> map : maps) { | |
| 365 PropertyAccessInfo access_info; | |
| 366 if (!ComputePropertyAccessInfo(map, name, &access_info)) return false; | |
| 367 access_infos->push_back(access_info); | |
| 368 } | |
| 369 return true; | |
| 370 } | |
| 371 | |
| 372 | |
| 373 Reduction JSGlobalSpecialization::ReduceJSLoadNamed(Node* node) { | |
| 374 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); | |
| 375 LoadNamedParameters const p = LoadNamedParametersOf(node->op()); | |
| 376 Handle<Name> name = p.name(); | |
| 377 Node* receiver = NodeProperties::GetValueInput(node, 0); | |
| 378 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | |
| 379 Node* effect = NodeProperties::GetEffectInput(node); | |
| 380 Node* control = NodeProperties::GetControlInput(node); | |
| 381 | |
| 382 // Not much we can do if deoptimization support is disabled. | |
| 383 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | |
| 384 | |
| 385 // Extract receiver maps from the LOAD_IC using the LoadICNexus. | |
| 386 MapHandleList receiver_maps; | |
| 387 if (!p.feedback().IsValid()) return NoChange(); | |
| 388 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); | |
| 389 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | |
| 390 DCHECK_LT(0, receiver_maps.length()); | |
| 391 | |
| 392 // Compute property access infos for the receiver maps. | |
| 393 ZoneVector<PropertyAccessInfo> access_infos(zone()); | |
| 394 if (!ComputePropertyAccessInfos(receiver_maps, name, &access_infos)) { | |
| 395 return NoChange(); | |
| 396 } | |
| 397 DCHECK(!access_infos.empty()); | |
| 398 | |
| 399 // The final states for every polymorphic branch. We join them with | |
| 400 // Merge+Phi+EffectPhi at the bottom. | |
| 401 ZoneVector<Node*> values(zone()); | |
| 402 ZoneVector<Node*> effects(zone()); | |
| 403 ZoneVector<Node*> controls(zone()); | |
| 404 | |
| 405 // The list of "exiting" controls, which currently go to a single deoptimize. | |
| 406 // TODO(bmeurer): Consider using an IC as fallback. | |
| 407 Node* const exit_effect = effect; | |
| 408 ZoneVector<Node*> exit_controls(zone()); | |
| 409 | |
| 410 // Ensure that {receiver} is a heap object. | |
| 411 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | |
| 412 Node* branch = | |
| 413 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); | |
| 414 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
| 415 control = graph()->NewNode(common()->IfFalse(), branch); | |
| 416 | |
| 417 // Load the {receiver} map. The resulting effect is the dominating effect for | |
| 418 // all (polymorphic) branches. | |
| 419 Node* receiver_map = effect = | |
| 420 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
| 421 receiver, effect, control); | |
| 422 | |
| 423 // Generate code for the various different property access patterns. | |
| 424 Node* fallthrough_control = control; | |
| 425 for (PropertyAccessInfo const& access_info : access_infos) { | |
| 426 Node* this_value = receiver; | |
| 427 Node* this_effect = effect; | |
| 428 Node* this_control; | |
| 429 | |
| 430 // Perform map check on {receiver}. | |
| 431 Type* receiver_type = access_info.receiver_type(); | |
| 432 if (receiver_type->Is(Type::String())) { | |
| 433 // Emit an instance type check for strings. | |
| 434 Node* receiver_instance_type = this_effect = graph()->NewNode( | |
| 435 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), | |
| 436 receiver_map, this_effect, fallthrough_control); | |
| 437 Node* check = | |
| 438 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, | |
| 439 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); | |
| 440 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 441 check, fallthrough_control); | |
| 442 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 443 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
| 444 } else { | |
| 445 // Emit a (sequence of) map checks for other properties. | |
| 446 ZoneVector<Node*> this_controls(zone()); | |
| 447 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | |
| 448 i.Advance()) { | |
| 449 Handle<Map> map = i.Current(); | |
| 450 Node* check = | |
| 451 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
| 452 receiver_map, jsgraph()->Constant(map)); | |
| 453 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 454 check, fallthrough_control); | |
| 455 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
| 456 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 457 } | |
| 458 int const this_control_count = static_cast<int>(this_controls.size()); | |
| 459 this_control = | |
| 460 (this_control_count == 1) | |
| 461 ? this_controls.front() | |
| 462 : graph()->NewNode(common()->Merge(this_control_count), | |
| 463 this_control_count, &this_controls.front()); | |
| 464 } | |
| 465 | |
| 466 // Determine actual holder and perform prototype chain checks. | |
| 467 Handle<JSObject> holder; | |
| 468 if (access_info.holder().ToHandle(&holder)) { | |
| 469 this_value = jsgraph()->Constant(holder); | |
| 470 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | |
| 471 i.Advance()) { | |
| 472 Handle<Map> map = i.Current(); | |
| 473 PrototypeIterator j(map); | |
| 474 while (true) { | |
| 475 // Check that the {prototype} still has the same map. For stable | |
| 476 // maps, we can add a stability dependency on the prototype map; | |
| 477 // for everything else we need to perform a map check at runtime. | |
| 478 Handle<JSReceiver> prototype = | |
| 479 PrototypeIterator::GetCurrent<JSReceiver>(j); | |
| 480 if (prototype->map()->is_stable()) { | |
| 481 dependencies()->AssumeMapStable( | |
| 482 handle(prototype->map(), isolate())); | |
| 483 } else { | |
| 484 Node* prototype_map = this_effect = graph()->NewNode( | |
| 485 simplified()->LoadField(AccessBuilder::ForMap()), | |
| 486 jsgraph()->Constant(prototype), this_effect, this_control); | |
| 487 Node* check = graph()->NewNode( | |
| 488 simplified()->ReferenceEqual(Type::Internal()), prototype_map, | |
| 489 jsgraph()->Constant(handle(prototype->map(), isolate()))); | |
| 490 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 491 check, this_control); | |
| 492 exit_controls.push_back( | |
| 493 graph()->NewNode(common()->IfFalse(), branch)); | |
| 494 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
| 495 } | |
| 496 // Stop once we get to the holder. | |
| 497 if (prototype.is_identical_to(holder)) break; | |
| 498 j.Advance(); | |
| 499 } | |
| 500 } | |
| 501 } | |
| 502 | |
| 503 // Generate the actual property access. | |
| 504 if (access_info.IsDataConstant()) { | |
| 505 this_value = jsgraph()->Constant(access_info.constant()); | |
| 506 } else { | |
| 507 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some | |
| 508 // common code once we also have support for stores. | |
| 509 DCHECK(access_info.IsData()); | |
| 510 FieldIndex const field_index = access_info.field_index(); | |
| 511 Representation const field_representation = | |
| 512 access_info.field_representation(); | |
| 513 if (!field_index.is_inobject()) { | |
| 514 this_value = this_effect = graph()->NewNode( | |
| 515 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | |
| 516 this_value, this_effect, this_control); | |
| 517 } | |
| 518 FieldAccess field_access; | |
| 519 field_access.base_is_tagged = kTaggedBase; | |
| 520 field_access.offset = field_index.offset(); | |
| 521 field_access.name = name; | |
| 522 field_access.type = Type::Any(); | |
| 523 field_access.machine_type = kMachAnyTagged; | |
| 524 if (field_representation.IsSmi()) { | |
| 525 field_access.type = Type::Intersect( | |
| 526 Type::SignedSmall(), Type::TaggedSigned(), graph()->zone()); | |
| 527 } else if (field_representation.IsDouble()) { | |
| 528 if (!field_index.is_inobject() || field_index.is_hidden_field() || | |
| 529 !FLAG_unbox_double_fields) { | |
| 530 this_value = this_effect = | |
| 531 graph()->NewNode(simplified()->LoadField(field_access), | |
| 532 this_value, this_effect, this_control); | |
| 533 field_access.offset = HeapNumber::kValueOffset; | |
| 534 field_access.name = MaybeHandle<Name>(); | |
| 535 } | |
| 536 field_access.type = Type::Intersect( | |
| 537 Type::Number(), Type::UntaggedFloat64(), graph()->zone()); | |
| 538 field_access.machine_type = kMachFloat64; | |
| 539 } else if (field_representation.IsHeapObject()) { | |
| 540 field_access.type = Type::TaggedPointer(); | |
| 541 } | |
| 542 this_value = this_effect = | |
| 543 graph()->NewNode(simplified()->LoadField(field_access), this_value, | |
| 544 this_effect, this_control); | |
| 545 } | |
| 546 | |
| 547 // Remember the final state for this property access. | |
| 548 values.push_back(this_value); | |
| 549 effects.push_back(this_effect); | |
| 550 controls.push_back(this_control); | |
| 551 } | |
| 552 | |
| 553 // Collect the fallthru control as final "exit" control. | |
| 554 exit_controls.push_back(fallthrough_control); | |
| 555 | |
| 556 // TODO(bmeurer/mtrofin): Splintering cannot currently deal with deferred | |
| 557 // blocks that contain only a single non-deoptimize instruction (i.e. a | |
| 558 // jump). Generating a single Merge here, which joins all the deoptimizing | |
| 559 // controls would generate a lot of these basic blocks, however. So this | |
| 560 // is disabled for now until splintering is fixed. | |
| 561 #if 0 | |
| 562 // Generate the single "exit" point, where we get if either all map/instance | |
| 563 // type checks failed, or one of the assumptions inside one of the cases | |
| 564 // failes (i.e. failing prototype chain check). | |
| 565 // TODO(bmeurer): Consider falling back to IC here if deoptimization is | |
| 566 // disabled. | |
| 567 int const exit_control_count = static_cast<int>(exit_controls.size()); | |
| 568 Node* exit_control = | |
| 569 (exit_control_count == 1) | |
| 570 ? exit_controls.front() | |
| 571 : graph()->NewNode(common()->Merge(exit_control_count), | |
| 572 exit_control_count, &exit_controls.front()); | |
| 573 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
| 574 exit_effect, exit_control); | |
| 575 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
| 576 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
| 577 #else | |
| 578 for (Node* const exit_control : exit_controls) { | |
| 579 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
| 580 exit_effect, exit_control); | |
| 581 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
| 582 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
| 583 } | |
| 584 #endif | |
| 585 | |
| 586 // Generate the final merge point for all (polymorphic) branches. | |
| 587 Node* value; | |
| 588 int const control_count = static_cast<int>(controls.size()); | |
| 589 if (control_count == 1) { | |
| 590 value = values.front(); | |
| 591 effect = effects.front(); | |
| 592 control = controls.front(); | |
| 593 } else { | |
| 594 control = graph()->NewNode(common()->Merge(control_count), control_count, | |
| 595 &controls.front()); | |
| 596 values.push_back(control); | |
| 597 value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count), | |
| 598 control_count + 1, &values.front()); | |
| 599 effects.push_back(control); | |
| 600 effect = graph()->NewNode(common()->EffectPhi(control_count), | |
| 601 control_count + 1, &effects.front()); | |
| 602 } | |
| 603 return Replace(node, value, effect, control); | |
| 604 } | |
| 605 | |
| 606 | |
| 607 Reduction JSGlobalSpecialization::Replace(Node* node, Handle<Object> value) { | |
| 608 return Replace(node, jsgraph()->Constant(value)); | |
| 609 } | |
| 610 | |
| 611 | |
| 612 bool JSGlobalSpecialization::LookupInScriptContextTable( | |
| 613 Handle<Name> name, ScriptContextTableLookupResult* result) { | |
| 614 if (!name->IsString()) return false; | |
| 615 Handle<ScriptContextTable> script_context_table( | |
| 616 global_object()->native_context()->script_context_table()); | |
| 617 ScriptContextTable::LookupResult lookup_result; | |
| 618 if (!ScriptContextTable::Lookup(script_context_table, | |
| 619 Handle<String>::cast(name), &lookup_result)) { | |
| 620 return false; | |
| 621 } | |
| 622 Handle<Context> script_context = ScriptContextTable::GetContext( | |
| 623 script_context_table, lookup_result.context_index); | |
| 624 if (script_context->is_the_hole(lookup_result.slot_index)) return false; | |
| 625 result->context = script_context; | |
| 626 result->immutable = IsImmutableVariableMode(lookup_result.mode); | |
| 627 result->index = lookup_result.slot_index; | |
| 628 return true; | |
| 629 } | |
| 630 | |
| 631 | |
| 632 Graph* JSGlobalSpecialization::graph() const { return jsgraph()->graph(); } | |
| 633 | |
| 634 | |
| 635 Isolate* JSGlobalSpecialization::isolate() const { | |
| 636 return jsgraph()->isolate(); | |
| 637 } | |
| 638 | |
| 639 | |
| 640 MachineOperatorBuilder* JSGlobalSpecialization::machine() const { | |
| 641 return jsgraph()->machine(); | |
| 642 } | |
| 643 | |
| 644 | |
| 645 CommonOperatorBuilder* JSGlobalSpecialization::common() const { | |
| 646 return jsgraph()->common(); | |
| 647 } | |
| 648 | |
| 649 | |
| 650 JSOperatorBuilder* JSGlobalSpecialization::javascript() const { | |
| 651 return jsgraph()->javascript(); | |
| 652 } | |
| 653 | |
| 654 | |
| 655 SimplifiedOperatorBuilder* JSGlobalSpecialization::simplified() const { | |
| 656 return jsgraph()->simplified(); | |
| 657 } | |
| 658 | |
| 659 } // namespace compiler | |
| 660 } // namespace internal | |
| 661 } // namespace v8 | |
| OLD | NEW |