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-native-context-specialization.h" 6 7#include "src/api/api-inl.h" 8#include "src/base/optional.h" 9#include "src/builtins/accessors.h" 10#include "src/codegen/code-factory.h" 11#include "src/codegen/string-constants.h" 12#include "src/compiler/access-builder.h" 13#include "src/compiler/access-info.h" 14#include "src/compiler/allocation-builder-inl.h" 15#include "src/compiler/allocation-builder.h" 16#include "src/compiler/compilation-dependencies.h" 17#include "src/compiler/js-graph.h" 18#include "src/compiler/js-operator.h" 19#include "src/compiler/linkage.h" 20#include "src/compiler/map-inference.h" 21#include "src/compiler/node-matchers.h" 22#include "src/compiler/property-access-builder.h" 23#include "src/compiler/type-cache.h" 24#include "src/execution/isolate-inl.h" 25#include "src/objects/feedback-vector.h" 26#include "src/objects/field-index-inl.h" 27#include "src/objects/heap-number.h" 28#include "src/objects/js-array-buffer-inl.h" 29#include "src/objects/js-array-inl.h" 30#include "src/objects/templates.h" 31 32namespace v8 { 33namespace internal { 34namespace compiler { 35 36namespace { 37 38bool HasNumberMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps) { 39 for (MapRef map : maps) { 40 if (map.IsHeapNumberMap()) return true; 41 } 42 return false; 43} 44 45bool HasOnlyJSArrayMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps) { 46 for (MapRef map : maps) { 47 if (!map.IsJSArrayMap()) return false; 48 } 49 return true; 50} 51 52} // namespace 53 54JSNativeContextSpecialization::JSNativeContextSpecialization( 55 Editor* editor, JSGraph* jsgraph, JSHeapBroker* broker, Flags flags, 56 CompilationDependencies* dependencies, Zone* zone, Zone* shared_zone) 57 : AdvancedReducer(editor), 58 jsgraph_(jsgraph), 59 broker_(broker), 60 flags_(flags), 61 global_object_(broker->target_native_context().global_object().object()), 62 global_proxy_( 63 broker->target_native_context().global_proxy_object().object()), 64 dependencies_(dependencies), 65 zone_(zone), 66 shared_zone_(shared_zone), 67 type_cache_(TypeCache::Get()) {} 68 69Reduction JSNativeContextSpecialization::Reduce(Node* node) { 70 switch (node->opcode()) { 71 case IrOpcode::kJSAdd: 72 return ReduceJSAdd(node); 73 case IrOpcode::kJSAsyncFunctionEnter: 74 return ReduceJSAsyncFunctionEnter(node); 75 case IrOpcode::kJSAsyncFunctionReject: 76 return ReduceJSAsyncFunctionReject(node); 77 case IrOpcode::kJSAsyncFunctionResolve: 78 return ReduceJSAsyncFunctionResolve(node); 79 case IrOpcode::kJSGetSuperConstructor: 80 return ReduceJSGetSuperConstructor(node); 81 case IrOpcode::kJSInstanceOf: 82 return ReduceJSInstanceOf(node); 83 case IrOpcode::kJSHasInPrototypeChain: 84 return ReduceJSHasInPrototypeChain(node); 85 case IrOpcode::kJSOrdinaryHasInstance: 86 return ReduceJSOrdinaryHasInstance(node); 87 case IrOpcode::kJSPromiseResolve: 88 return ReduceJSPromiseResolve(node); 89 case IrOpcode::kJSResolvePromise: 90 return ReduceJSResolvePromise(node); 91 case IrOpcode::kJSLoadGlobal: 92 return ReduceJSLoadGlobal(node); 93 case IrOpcode::kJSStoreGlobal: 94 return ReduceJSStoreGlobal(node); 95 case IrOpcode::kJSLoadNamed: 96 return ReduceJSLoadNamed(node); 97 case IrOpcode::kJSLoadNamedFromSuper: 98 return ReduceJSLoadNamedFromSuper(node); 99 case IrOpcode::kJSSetNamedProperty: 100 return ReduceJSSetNamedProperty(node); 101 case IrOpcode::kJSHasProperty: 102 return ReduceJSHasProperty(node); 103 case IrOpcode::kJSLoadProperty: 104 return ReduceJSLoadProperty(node); 105 case IrOpcode::kJSSetKeyedProperty: 106 return ReduceJSSetKeyedProperty(node); 107 case IrOpcode::kJSDefineKeyedOwnProperty: 108 return ReduceJSDefineKeyedOwnProperty(node); 109 case IrOpcode::kJSDefineNamedOwnProperty: 110 return ReduceJSDefineNamedOwnProperty(node); 111 case IrOpcode::kJSDefineKeyedOwnPropertyInLiteral: 112 return ReduceJSDefineKeyedOwnPropertyInLiteral(node); 113 case IrOpcode::kJSStoreInArrayLiteral: 114 return ReduceJSStoreInArrayLiteral(node); 115 case IrOpcode::kJSToObject: 116 return ReduceJSToObject(node); 117 case IrOpcode::kJSToString: 118 return ReduceJSToString(node); 119 case IrOpcode::kJSGetIterator: 120 return ReduceJSGetIterator(node); 121 default: 122 break; 123 } 124 return NoChange(); 125} 126 127// static 128base::Optional<size_t> JSNativeContextSpecialization::GetMaxStringLength( 129 JSHeapBroker* broker, Node* node) { 130 if (node->opcode() == IrOpcode::kDelayedStringConstant) { 131 return StringConstantBaseOf(node->op())->GetMaxStringConstantLength(); 132 } 133 134 HeapObjectMatcher matcher(node); 135 if (matcher.HasResolvedValue() && matcher.Ref(broker).IsString()) { 136 StringRef input = matcher.Ref(broker).AsString(); 137 return input.length(); 138 } 139 140 NumberMatcher number_matcher(node); 141 if (number_matcher.HasResolvedValue()) { 142 return kMaxDoubleStringLength; 143 } 144 145 // We don't support objects with possibly monkey-patched prototype.toString 146 // as it might have side-effects, so we shouldn't attempt lowering them. 147 return base::nullopt; 148} 149 150Reduction JSNativeContextSpecialization::ReduceJSToString(Node* node) { 151 DCHECK_EQ(IrOpcode::kJSToString, node->opcode()); 152 Node* const input = node->InputAt(0); 153 Reduction reduction; 154 155 HeapObjectMatcher matcher(input); 156 if (matcher.HasResolvedValue() && matcher.Ref(broker()).IsString()) { 157 reduction = Changed(input); // JSToString(x:string) => x 158 ReplaceWithValue(node, reduction.replacement()); 159 return reduction; 160 } 161 162 // TODO(turbofan): This optimization is weaker than what we used to have 163 // in js-typed-lowering for OrderedNumbers. We don't have types here though, 164 // so alternative approach should be designed if this causes performance 165 // regressions and the stronger optimization should be re-implemented. 166 NumberMatcher number_matcher(input); 167 if (number_matcher.HasResolvedValue()) { 168 const StringConstantBase* base = shared_zone()->New<NumberToStringConstant>( 169 number_matcher.ResolvedValue()); 170 reduction = 171 Replace(graph()->NewNode(common()->DelayedStringConstant(base))); 172 ReplaceWithValue(node, reduction.replacement()); 173 return reduction; 174 } 175 176 return NoChange(); 177} 178 179base::Optional<const StringConstantBase*> 180JSNativeContextSpecialization::CreateDelayedStringConstant(Node* node) { 181 if (node->opcode() == IrOpcode::kDelayedStringConstant) { 182 return StringConstantBaseOf(node->op()); 183 } else { 184 NumberMatcher number_matcher(node); 185 if (number_matcher.HasResolvedValue()) { 186 return shared_zone()->New<NumberToStringConstant>( 187 number_matcher.ResolvedValue()); 188 } else { 189 HeapObjectMatcher matcher(node); 190 if (matcher.HasResolvedValue() && matcher.Ref(broker()).IsString()) { 191 StringRef s = matcher.Ref(broker()).AsString(); 192 if (!s.length().has_value()) return base::nullopt; 193 return shared_zone()->New<StringLiteral>( 194 s.object(), static_cast<size_t>(s.length().value())); 195 } else { 196 UNREACHABLE(); 197 } 198 } 199 } 200} 201 202namespace { 203bool IsStringConstant(JSHeapBroker* broker, Node* node) { 204 if (node->opcode() == IrOpcode::kDelayedStringConstant) { 205 return true; 206 } 207 208 HeapObjectMatcher matcher(node); 209 return matcher.HasResolvedValue() && matcher.Ref(broker).IsString(); 210} 211} // namespace 212 213Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionEnter( 214 Node* node) { 215 DCHECK_EQ(IrOpcode::kJSAsyncFunctionEnter, node->opcode()); 216 Node* closure = NodeProperties::GetValueInput(node, 0); 217 Node* receiver = NodeProperties::GetValueInput(node, 1); 218 Node* context = NodeProperties::GetContextInput(node); 219 Node* frame_state = NodeProperties::GetFrameStateInput(node); 220 Node* effect = NodeProperties::GetEffectInput(node); 221 Node* control = NodeProperties::GetControlInput(node); 222 223 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange(); 224 225 // Create the promise for the async function. 226 Node* promise = effect = 227 graph()->NewNode(javascript()->CreatePromise(), context, effect); 228 229 // Create the JSAsyncFunctionObject based on the SharedFunctionInfo 230 // extracted from the top-most frame in {frame_state}. 231 SharedFunctionInfoRef shared = MakeRef( 232 broker(), 233 FrameStateInfoOf(frame_state->op()).shared_info().ToHandleChecked()); 234 DCHECK(shared.is_compiled()); 235 int register_count = 236 shared.internal_formal_parameter_count_without_receiver() + 237 shared.GetBytecodeArray().register_count(); 238 MapRef fixed_array_map = MakeRef(broker(), factory()->fixed_array_map()); 239 AllocationBuilder ab(jsgraph(), effect, control); 240 if (!ab.CanAllocateArray(register_count, fixed_array_map)) { 241 return NoChange(); 242 } 243 Node* value = effect = 244 graph()->NewNode(javascript()->CreateAsyncFunctionObject(register_count), 245 closure, receiver, promise, context, effect, control); 246 ReplaceWithValue(node, value, effect, control); 247 return Replace(value); 248} 249 250Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionReject( 251 Node* node) { 252 DCHECK_EQ(IrOpcode::kJSAsyncFunctionReject, node->opcode()); 253 Node* async_function_object = NodeProperties::GetValueInput(node, 0); 254 Node* reason = NodeProperties::GetValueInput(node, 1); 255 Node* context = NodeProperties::GetContextInput(node); 256 Node* frame_state = NodeProperties::GetFrameStateInput(node); 257 Node* effect = NodeProperties::GetEffectInput(node); 258 Node* control = NodeProperties::GetControlInput(node); 259 260 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange(); 261 262 // Load the promise from the {async_function_object}. 263 Node* promise = effect = graph()->NewNode( 264 simplified()->LoadField(AccessBuilder::ForJSAsyncFunctionObjectPromise()), 265 async_function_object, effect, control); 266 267 // Create a nested frame state inside the current method's most-recent 268 // {frame_state} that will ensure that lazy deoptimizations at this 269 // point will still return the {promise} instead of the result of the 270 // JSRejectPromise operation (which yields undefined). 271 Node* parameters[] = {promise}; 272 frame_state = CreateStubBuiltinContinuationFrameState( 273 jsgraph(), Builtin::kAsyncFunctionLazyDeoptContinuation, context, 274 parameters, arraysize(parameters), frame_state, 275 ContinuationFrameStateMode::LAZY); 276 277 // Disable the additional debug event for the rejection since a 278 // debug event already happend for the exception that got us here. 279 Node* debug_event = jsgraph()->FalseConstant(); 280 effect = graph()->NewNode(javascript()->RejectPromise(), promise, reason, 281 debug_event, context, frame_state, effect, control); 282 ReplaceWithValue(node, promise, effect, control); 283 return Replace(promise); 284} 285 286Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionResolve( 287 Node* node) { 288 DCHECK_EQ(IrOpcode::kJSAsyncFunctionResolve, node->opcode()); 289 Node* async_function_object = NodeProperties::GetValueInput(node, 0); 290 Node* value = NodeProperties::GetValueInput(node, 1); 291 Node* context = NodeProperties::GetContextInput(node); 292 Node* frame_state = NodeProperties::GetFrameStateInput(node); 293 Node* effect = NodeProperties::GetEffectInput(node); 294 Node* control = NodeProperties::GetControlInput(node); 295 296 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange(); 297 298 // Load the promise from the {async_function_object}. 299 Node* promise = effect = graph()->NewNode( 300 simplified()->LoadField(AccessBuilder::ForJSAsyncFunctionObjectPromise()), 301 async_function_object, effect, control); 302 303 // Create a nested frame state inside the current method's most-recent 304 // {frame_state} that will ensure that lazy deoptimizations at this 305 // point will still return the {promise} instead of the result of the 306 // JSResolvePromise operation (which yields undefined). 307 Node* parameters[] = {promise}; 308 frame_state = CreateStubBuiltinContinuationFrameState( 309 jsgraph(), Builtin::kAsyncFunctionLazyDeoptContinuation, context, 310 parameters, arraysize(parameters), frame_state, 311 ContinuationFrameStateMode::LAZY); 312 313 effect = graph()->NewNode(javascript()->ResolvePromise(), promise, value, 314 context, frame_state, effect, control); 315 ReplaceWithValue(node, promise, effect, control); 316 return Replace(promise); 317} 318 319Reduction JSNativeContextSpecialization::ReduceJSAdd(Node* node) { 320 // TODO(turbofan): This has to run together with the inlining and 321 // native context specialization to be able to leverage the string 322 // constant-folding for optimizing property access, but we should 323 // nevertheless find a better home for this at some point. 324 DCHECK_EQ(IrOpcode::kJSAdd, node->opcode()); 325 326 Node* const lhs = node->InputAt(0); 327 Node* const rhs = node->InputAt(1); 328 329 base::Optional<size_t> lhs_len = GetMaxStringLength(broker(), lhs); 330 base::Optional<size_t> rhs_len = GetMaxStringLength(broker(), rhs); 331 if (!lhs_len || !rhs_len) { 332 return NoChange(); 333 } 334 335 // Fold into DelayedStringConstant if at least one of the parameters is a 336 // string constant and the addition won't throw due to too long result. 337 if (*lhs_len + *rhs_len <= String::kMaxLength && 338 (IsStringConstant(broker(), lhs) || IsStringConstant(broker(), rhs))) { 339 base::Optional<const StringConstantBase*> left = 340 CreateDelayedStringConstant(lhs); 341 if (!left.has_value()) return NoChange(); 342 base::Optional<const StringConstantBase*> right = 343 CreateDelayedStringConstant(rhs); 344 if (!right.has_value()) return NoChange(); 345 const StringConstantBase* cons = 346 shared_zone()->New<StringCons>(left.value(), right.value()); 347 348 Node* reduced = graph()->NewNode(common()->DelayedStringConstant(cons)); 349 ReplaceWithValue(node, reduced); 350 return Replace(reduced); 351 } 352 353 return NoChange(); 354} 355 356Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor( 357 Node* node) { 358 DCHECK_EQ(IrOpcode::kJSGetSuperConstructor, node->opcode()); 359 Node* constructor = NodeProperties::GetValueInput(node, 0); 360 361 // Check if the input is a known JSFunction. 362 HeapObjectMatcher m(constructor); 363 if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSFunction()) { 364 return NoChange(); 365 } 366 JSFunctionRef function = m.Ref(broker()).AsJSFunction(); 367 MapRef function_map = function.map(); 368 HeapObjectRef function_prototype = function_map.prototype(); 369 370 // We can constant-fold the super constructor access if the 371 // {function}s map is stable, i.e. we can use a code dependency 372 // to guard against [[Prototype]] changes of {function}. 373 if (function_map.is_stable()) { 374 dependencies()->DependOnStableMap(function_map); 375 Node* value = jsgraph()->Constant(function_prototype); 376 ReplaceWithValue(node, value); 377 return Replace(value); 378 } 379 380 return NoChange(); 381} 382 383Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { 384 JSInstanceOfNode n(node); 385 FeedbackParameter const& p = n.Parameters(); 386 Node* object = n.left(); 387 Node* constructor = n.right(); 388 TNode<Object> context = n.context(); 389 FrameState frame_state = n.frame_state(); 390 Effect effect = n.effect(); 391 Control control = n.control(); 392 393 // Check if the right hand side is a known {receiver}, or 394 // we have feedback from the InstanceOfIC. 395 base::Optional<JSObjectRef> receiver; 396 HeapObjectMatcher m(constructor); 397 if (m.HasResolvedValue() && m.Ref(broker()).IsJSObject()) { 398 receiver = m.Ref(broker()).AsJSObject(); 399 } else if (p.feedback().IsValid()) { 400 ProcessedFeedback const& feedback = 401 broker()->GetFeedbackForInstanceOf(FeedbackSource(p.feedback())); 402 if (feedback.IsInsufficient()) return NoChange(); 403 receiver = feedback.AsInstanceOf().value(); 404 } else { 405 return NoChange(); 406 } 407 408 if (!receiver.has_value()) return NoChange(); 409 410 MapRef receiver_map = receiver->map(); 411 NameRef name = MakeRef(broker(), isolate()->factory()->has_instance_symbol()); 412 PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo( 413 receiver_map, name, AccessMode::kLoad, dependencies()); 414 415 // TODO(v8:11457) Support dictionary mode holders here. 416 if (access_info.IsInvalid() || access_info.HasDictionaryHolder()) { 417 return NoChange(); 418 } 419 access_info.RecordDependencies(dependencies()); 420 421 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies()); 422 423 if (access_info.IsNotFound()) { 424 // If there's no @@hasInstance handler, the OrdinaryHasInstance operation 425 // takes over, but that requires the constructor to be callable. 426 if (!receiver_map.is_callable()) return NoChange(); 427 428 dependencies()->DependOnStablePrototypeChains( 429 access_info.lookup_start_object_maps(), kStartAtPrototype); 430 431 // Monomorphic property access. 432 access_builder.BuildCheckMaps(constructor, &effect, control, 433 access_info.lookup_start_object_maps()); 434 435 // Lower to OrdinaryHasInstance(C, O). 436 NodeProperties::ReplaceValueInput(node, constructor, 0); 437 NodeProperties::ReplaceValueInput(node, object, 1); 438 NodeProperties::ReplaceEffectInput(node, effect); 439 STATIC_ASSERT(n.FeedbackVectorIndex() == 2); 440 node->RemoveInput(n.FeedbackVectorIndex()); 441 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); 442 return Changed(node).FollowedBy(ReduceJSOrdinaryHasInstance(node)); 443 } 444 445 if (access_info.IsFastDataConstant()) { 446 base::Optional<JSObjectRef> holder = access_info.holder(); 447 bool found_on_proto = holder.has_value(); 448 JSObjectRef holder_ref = found_on_proto ? holder.value() : receiver.value(); 449 base::Optional<ObjectRef> constant = holder_ref.GetOwnFastDataProperty( 450 access_info.field_representation(), access_info.field_index(), 451 dependencies()); 452 if (!constant.has_value() || !constant->IsHeapObject() || 453 !constant->AsHeapObject().map().is_callable()) { 454 return NoChange(); 455 } 456 457 if (found_on_proto) { 458 dependencies()->DependOnStablePrototypeChains( 459 access_info.lookup_start_object_maps(), kStartAtPrototype, 460 holder.value()); 461 } 462 463 // Check that {constructor} is actually {receiver}. 464 constructor = access_builder.BuildCheckValue(constructor, &effect, control, 465 receiver->object()); 466 467 // Monomorphic property access. 468 access_builder.BuildCheckMaps(constructor, &effect, control, 469 access_info.lookup_start_object_maps()); 470 471 // Create a nested frame state inside the current method's most-recent frame 472 // state that will ensure that deopts that happen after this point will not 473 // fallback to the last Checkpoint--which would completely re-execute the 474 // instanceof logic--but rather create an activation of a version of the 475 // ToBoolean stub that finishes the remaining work of instanceof and returns 476 // to the caller without duplicating side-effects upon a lazy deopt. 477 Node* continuation_frame_state = CreateStubBuiltinContinuationFrameState( 478 jsgraph(), Builtin::kToBooleanLazyDeoptContinuation, context, nullptr, 479 0, frame_state, ContinuationFrameStateMode::LAZY); 480 481 // Call the @@hasInstance handler. 482 Node* target = jsgraph()->Constant(*constant); 483 Node* feedback = jsgraph()->UndefinedConstant(); 484 // Value inputs plus context, frame state, effect, control. 485 STATIC_ASSERT(JSCallNode::ArityForArgc(1) + 4 == 8); 486 node->EnsureInputCount(graph()->zone(), 8); 487 node->ReplaceInput(JSCallNode::TargetIndex(), target); 488 node->ReplaceInput(JSCallNode::ReceiverIndex(), constructor); 489 node->ReplaceInput(JSCallNode::ArgumentIndex(0), object); 490 node->ReplaceInput(3, feedback); 491 node->ReplaceInput(4, context); 492 node->ReplaceInput(5, continuation_frame_state); 493 node->ReplaceInput(6, effect); 494 node->ReplaceInput(7, control); 495 NodeProperties::ChangeOp( 496 node, javascript()->Call(JSCallNode::ArityForArgc(1), CallFrequency(), 497 FeedbackSource(), 498 ConvertReceiverMode::kNotNullOrUndefined)); 499 500 // Rewire the value uses of {node} to ToBoolean conversion of the result. 501 Node* value = graph()->NewNode(simplified()->ToBoolean(), node); 502 for (Edge edge : node->use_edges()) { 503 if (NodeProperties::IsValueEdge(edge) && edge.from() != value) { 504 edge.UpdateTo(value); 505 Revisit(edge.from()); 506 } 507 } 508 return Changed(node); 509 } 510 511 return NoChange(); 512} 513 514JSNativeContextSpecialization::InferHasInPrototypeChainResult 515JSNativeContextSpecialization::InferHasInPrototypeChain( 516 Node* receiver, Effect effect, HeapObjectRef const& prototype) { 517 ZoneRefUnorderedSet<MapRef> receiver_maps(zone()); 518 NodeProperties::InferMapsResult result = NodeProperties::InferMapsUnsafe( 519 broker(), receiver, effect, &receiver_maps); 520 if (result == NodeProperties::kNoMaps) return kMayBeInPrototypeChain; 521 522 ZoneVector<MapRef> receiver_map_refs(zone()); 523 524 // Try to determine either that all of the {receiver_maps} have the given 525 // {prototype} in their chain, or that none do. If we can't tell, return 526 // kMayBeInPrototypeChain. 527 bool all = true; 528 bool none = true; 529 for (MapRef map : receiver_maps) { 530 receiver_map_refs.push_back(map); 531 if (result == NodeProperties::kUnreliableMaps && !map.is_stable()) { 532 return kMayBeInPrototypeChain; 533 } 534 while (true) { 535 if (IsSpecialReceiverInstanceType(map.instance_type())) { 536 return kMayBeInPrototypeChain; 537 } 538 if (!map.IsJSObjectMap()) { 539 all = false; 540 break; 541 } 542 HeapObjectRef map_prototype = map.prototype(); 543 if (map_prototype.equals(prototype)) { 544 none = false; 545 break; 546 } 547 map = map_prototype.map(); 548 // TODO(v8:11457) Support dictionary mode protoypes here. 549 if (!map.is_stable() || map.is_dictionary_map()) { 550 return kMayBeInPrototypeChain; 551 } 552 if (map.oddball_type() == OddballType::kNull) { 553 all = false; 554 break; 555 } 556 } 557 } 558 DCHECK_IMPLIES(all, !none); 559 if (!all && !none) return kMayBeInPrototypeChain; 560 561 { 562 base::Optional<JSObjectRef> last_prototype; 563 if (all) { 564 // We don't need to protect the full chain if we found the prototype, we 565 // can stop at {prototype}. In fact we could stop at the one before 566 // {prototype} but since we're dealing with multiple receiver maps this 567 // might be a different object each time, so it's much simpler to include 568 // {prototype}. That does, however, mean that we must check {prototype}'s 569 // map stability. 570 if (!prototype.map().is_stable()) return kMayBeInPrototypeChain; 571 last_prototype = prototype.AsJSObject(); 572 } 573 WhereToStart start = result == NodeProperties::kUnreliableMaps 574 ? kStartAtReceiver 575 : kStartAtPrototype; 576 dependencies()->DependOnStablePrototypeChains(receiver_map_refs, start, 577 last_prototype); 578 } 579 580 DCHECK_EQ(all, !none); 581 return all ? kIsInPrototypeChain : kIsNotInPrototypeChain; 582} 583 584Reduction JSNativeContextSpecialization::ReduceJSHasInPrototypeChain( 585 Node* node) { 586 DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode()); 587 Node* value = NodeProperties::GetValueInput(node, 0); 588 Node* prototype = NodeProperties::GetValueInput(node, 1); 589 Effect effect{NodeProperties::GetEffectInput(node)}; 590 591 // Check if we can constant-fold the prototype chain walk 592 // for the given {value} and the {prototype}. 593 HeapObjectMatcher m(prototype); 594 if (m.HasResolvedValue()) { 595 InferHasInPrototypeChainResult result = 596 InferHasInPrototypeChain(value, effect, m.Ref(broker())); 597 if (result != kMayBeInPrototypeChain) { 598 Node* result_in_chain = 599 jsgraph()->BooleanConstant(result == kIsInPrototypeChain); 600 ReplaceWithValue(node, result_in_chain); 601 return Replace(result_in_chain); 602 } 603 } 604 605 return NoChange(); 606} 607 608Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( 609 Node* node) { 610 DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); 611 Node* constructor = NodeProperties::GetValueInput(node, 0); 612 Node* object = NodeProperties::GetValueInput(node, 1); 613 614 // Check if the {constructor} is known at compile time. 615 HeapObjectMatcher m(constructor); 616 if (!m.HasResolvedValue()) return NoChange(); 617 618 if (m.Ref(broker()).IsJSBoundFunction()) { 619 // OrdinaryHasInstance on bound functions turns into a recursive invocation 620 // of the instanceof operator again. 621 JSBoundFunctionRef function = m.Ref(broker()).AsJSBoundFunction(); 622 Node* feedback = jsgraph()->UndefinedConstant(); 623 NodeProperties::ReplaceValueInput(node, object, 624 JSInstanceOfNode::LeftIndex()); 625 NodeProperties::ReplaceValueInput( 626 node, jsgraph()->Constant(function.bound_target_function()), 627 JSInstanceOfNode::RightIndex()); 628 node->InsertInput(zone(), JSInstanceOfNode::FeedbackVectorIndex(), 629 feedback); 630 NodeProperties::ChangeOp(node, javascript()->InstanceOf(FeedbackSource())); 631 return Changed(node).FollowedBy(ReduceJSInstanceOf(node)); 632 } 633 634 if (m.Ref(broker()).IsJSFunction()) { 635 // Optimize if we currently know the "prototype" property. 636 637 JSFunctionRef function = m.Ref(broker()).AsJSFunction(); 638 639 // TODO(neis): Remove the has_prototype_slot condition once the broker is 640 // always enabled. 641 if (!function.map().has_prototype_slot() || 642 !function.has_instance_prototype(dependencies()) || 643 function.PrototypeRequiresRuntimeLookup(dependencies())) { 644 return NoChange(); 645 } 646 647 ObjectRef prototype = dependencies()->DependOnPrototypeProperty(function); 648 Node* prototype_constant = jsgraph()->Constant(prototype); 649 650 // Lower the {node} to JSHasInPrototypeChain. 651 NodeProperties::ReplaceValueInput(node, object, 0); 652 NodeProperties::ReplaceValueInput(node, prototype_constant, 1); 653 NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain()); 654 return Changed(node).FollowedBy(ReduceJSHasInPrototypeChain(node)); 655 } 656 657 return NoChange(); 658} 659 660// ES section #sec-promise-resolve 661Reduction JSNativeContextSpecialization::ReduceJSPromiseResolve(Node* node) { 662 DCHECK_EQ(IrOpcode::kJSPromiseResolve, node->opcode()); 663 Node* constructor = NodeProperties::GetValueInput(node, 0); 664 Node* value = NodeProperties::GetValueInput(node, 1); 665 Node* context = NodeProperties::GetContextInput(node); 666 FrameState frame_state{NodeProperties::GetFrameStateInput(node)}; 667 Effect effect{NodeProperties::GetEffectInput(node)}; 668 Control control{NodeProperties::GetControlInput(node)}; 669 670 // Check if the {constructor} is the %Promise% function. 671 HeapObjectMatcher m(constructor); 672 if (!m.HasResolvedValue() || 673 !m.Ref(broker()).equals(native_context().promise_function())) { 674 return NoChange(); 675 } 676 677 // Only optimize if {value} cannot be a JSPromise. 678 MapInference inference(broker(), value, effect); 679 if (!inference.HaveMaps() || 680 inference.AnyOfInstanceTypesAre(JS_PROMISE_TYPE)) { 681 return NoChange(); 682 } 683 684 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange(); 685 686 // Create a %Promise% instance and resolve it with {value}. 687 Node* promise = effect = 688 graph()->NewNode(javascript()->CreatePromise(), context, effect); 689 effect = graph()->NewNode(javascript()->ResolvePromise(), promise, value, 690 context, frame_state, effect, control); 691 ReplaceWithValue(node, promise, effect, control); 692 return Replace(promise); 693} 694 695// ES section #sec-promise-resolve-functions 696Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) { 697 DCHECK_EQ(IrOpcode::kJSResolvePromise, node->opcode()); 698 Node* promise = NodeProperties::GetValueInput(node, 0); 699 Node* resolution = NodeProperties::GetValueInput(node, 1); 700 Node* context = NodeProperties::GetContextInput(node); 701 Effect effect{NodeProperties::GetEffectInput(node)}; 702 Control control{NodeProperties::GetControlInput(node)}; 703 704 // Check if we know something about the {resolution}. 705 MapInference inference(broker(), resolution, effect); 706 if (!inference.HaveMaps()) return NoChange(); 707 ZoneVector<MapRef> const& resolution_maps = inference.GetMaps(); 708 709 // Compute property access info for "then" on {resolution}. 710 ZoneVector<PropertyAccessInfo> access_infos(graph()->zone()); 711 AccessInfoFactory access_info_factory(broker(), dependencies(), 712 graph()->zone()); 713 714 for (const MapRef& map : resolution_maps) { 715 access_infos.push_back(broker()->GetPropertyAccessInfo( 716 map, MakeRef(broker(), isolate()->factory()->then_string()), 717 AccessMode::kLoad, dependencies())); 718 } 719 PropertyAccessInfo access_info = 720 access_info_factory.FinalizePropertyAccessInfosAsOne(access_infos, 721 AccessMode::kLoad); 722 723 // TODO(v8:11457) Support dictionary mode prototypes here. 724 if (access_info.IsInvalid() || access_info.HasDictionaryHolder()) { 725 return inference.NoChange(); 726 } 727 728 // Only optimize when {resolution} definitely doesn't have a "then" property. 729 if (!access_info.IsNotFound()) return inference.NoChange(); 730 731 if (!inference.RelyOnMapsViaStability(dependencies())) { 732 return inference.NoChange(); 733 } 734 735 dependencies()->DependOnStablePrototypeChains( 736 access_info.lookup_start_object_maps(), kStartAtPrototype); 737 738 // Simply fulfill the {promise} with the {resolution}. 739 Node* value = effect = 740 graph()->NewNode(javascript()->FulfillPromise(), promise, resolution, 741 context, effect, control); 742 ReplaceWithValue(node, value, effect, control); 743 return Replace(value); 744} 745 746namespace { 747 748FieldAccess ForPropertyCellValue(MachineRepresentation representation, 749 Type type, MaybeHandle<Map> map, 750 NameRef const& name) { 751 WriteBarrierKind kind = kFullWriteBarrier; 752 if (representation == MachineRepresentation::kTaggedSigned) { 753 kind = kNoWriteBarrier; 754 } else if (representation == MachineRepresentation::kTaggedPointer) { 755 kind = kPointerWriteBarrier; 756 } 757 MachineType r = MachineType::TypeForRepresentation(representation); 758 FieldAccess access = { 759 kTaggedBase, PropertyCell::kValueOffset, name.object(), map, type, r, 760 kind}; 761 return access; 762} 763 764} // namespace 765 766// TODO(neis): Try to merge this with ReduceNamedAccess by introducing a new 767// PropertyAccessInfo kind for global accesses and using the existing mechanism 768// for building loads/stores. 769// Note: The "receiver" parameter is only used for DCHECKS, but that's on 770// purpose. This way we can assert the super property access cases won't hit the 771// code which hasn't been modified to support super property access. 772Reduction JSNativeContextSpecialization::ReduceGlobalAccess( 773 Node* node, Node* lookup_start_object, Node* receiver, Node* value, 774 NameRef const& name, AccessMode access_mode, Node* key, 775 PropertyCellRef const& property_cell, Node* effect) { 776 if (!property_cell.Cache()) { 777 TRACE_BROKER_MISSING(broker(), "usable data for " << property_cell); 778 return NoChange(); 779 } 780 781 ObjectRef property_cell_value = property_cell.value(); 782 if (property_cell_value.IsHeapObject() && 783 property_cell_value.AsHeapObject().map().oddball_type() == 784 OddballType::kHole) { 785 // The property cell is no longer valid. 786 return NoChange(); 787 } 788 789 PropertyDetails property_details = property_cell.property_details(); 790 PropertyCellType property_cell_type = property_details.cell_type(); 791 DCHECK_EQ(PropertyKind::kData, property_details.kind()); 792 793 Node* control = NodeProperties::GetControlInput(node); 794 if (effect == nullptr) { 795 effect = NodeProperties::GetEffectInput(node); 796 } 797 798 // We have additional constraints for stores. 799 if (access_mode == AccessMode::kStore) { 800 DCHECK_EQ(receiver, lookup_start_object); 801 if (property_details.IsReadOnly()) { 802 // Don't even bother trying to lower stores to read-only data properties. 803 // TODO(neis): We could generate code that checks if the new value equals 804 // the old one and then does nothing or deopts, respectively. 805 return NoChange(); 806 } else if (property_cell_type == PropertyCellType::kUndefined) { 807 return NoChange(); 808 } else if (property_cell_type == PropertyCellType::kConstantType) { 809 // We rely on stability further below. 810 if (property_cell_value.IsHeapObject() && 811 !property_cell_value.AsHeapObject().map().is_stable()) { 812 return NoChange(); 813 } 814 } 815 } else if (access_mode == AccessMode::kHas) { 816 DCHECK_EQ(receiver, lookup_start_object); 817 // has checks cannot follow the fast-path used by loads when these 818 // conditions hold. 819 if ((property_details.IsConfigurable() || !property_details.IsReadOnly()) && 820 property_details.cell_type() != PropertyCellType::kConstant && 821 property_details.cell_type() != PropertyCellType::kUndefined) 822 return NoChange(); 823 } 824 825 // Ensure that {key} matches the specified {name} (if {key} is given). 826 if (key != nullptr) { 827 effect = BuildCheckEqualsName(name, key, effect, control); 828 } 829 830 // If we have a {lookup_start_object} to validate, we do so by checking that 831 // its map is the (target) global proxy's map. This guarantees that in fact 832 // the lookup start object is the global proxy. 833 // Note: we rely on the map constant below being the same as what is used in 834 // NativeContextRef::GlobalIsDetached(). 835 if (lookup_start_object != nullptr) { 836 effect = graph()->NewNode( 837 simplified()->CheckMaps( 838 CheckMapsFlag::kNone, 839 ZoneHandleSet<Map>( 840 native_context().global_proxy_object().map().object())), 841 lookup_start_object, effect, control); 842 } 843 844 if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) { 845 // Load from non-configurable, read-only data property on the global 846 // object can be constant-folded, even without deoptimization support. 847 if (!property_details.IsConfigurable() && property_details.IsReadOnly()) { 848 value = access_mode == AccessMode::kHas 849 ? jsgraph()->TrueConstant() 850 : jsgraph()->Constant(property_cell_value); 851 } else { 852 // Record a code dependency on the cell if we can benefit from the 853 // additional feedback, or the global property is configurable (i.e. 854 // can be deleted or reconfigured to an accessor property). 855 if (property_details.cell_type() != PropertyCellType::kMutable || 856 property_details.IsConfigurable()) { 857 dependencies()->DependOnGlobalProperty(property_cell); 858 } 859 860 // Load from constant/undefined global property can be constant-folded. 861 if (property_details.cell_type() == PropertyCellType::kConstant || 862 property_details.cell_type() == PropertyCellType::kUndefined) { 863 value = access_mode == AccessMode::kHas 864 ? jsgraph()->TrueConstant() 865 : jsgraph()->Constant(property_cell_value); 866 DCHECK(!property_cell_value.IsHeapObject() || 867 property_cell_value.AsHeapObject().map().oddball_type() != 868 OddballType::kHole); 869 } else { 870 DCHECK_NE(AccessMode::kHas, access_mode); 871 872 // Load from constant type cell can benefit from type feedback. 873 MaybeHandle<Map> map; 874 Type property_cell_value_type = Type::NonInternal(); 875 MachineRepresentation representation = MachineRepresentation::kTagged; 876 if (property_details.cell_type() == PropertyCellType::kConstantType) { 877 // Compute proper type based on the current value in the cell. 878 if (property_cell_value.IsSmi()) { 879 property_cell_value_type = Type::SignedSmall(); 880 representation = MachineRepresentation::kTaggedSigned; 881 } else if (property_cell_value.IsHeapNumber()) { 882 property_cell_value_type = Type::Number(); 883 representation = MachineRepresentation::kTaggedPointer; 884 } else { 885 MapRef property_cell_value_map = 886 property_cell_value.AsHeapObject().map(); 887 property_cell_value_type = Type::For(property_cell_value_map); 888 representation = MachineRepresentation::kTaggedPointer; 889 890 // We can only use the property cell value map for map check 891 // elimination if it's stable, i.e. the HeapObject wasn't 892 // mutated without the cell state being updated. 893 if (property_cell_value_map.is_stable()) { 894 dependencies()->DependOnStableMap(property_cell_value_map); 895 map = property_cell_value_map.object(); 896 } 897 } 898 } 899 value = effect = graph()->NewNode( 900 simplified()->LoadField(ForPropertyCellValue( 901 representation, property_cell_value_type, map, name)), 902 jsgraph()->Constant(property_cell), effect, control); 903 } 904 } 905 } else { 906 DCHECK_EQ(AccessMode::kStore, access_mode); 907 DCHECK_EQ(receiver, lookup_start_object); 908 DCHECK(!property_details.IsReadOnly()); 909 switch (property_details.cell_type()) { 910 case PropertyCellType::kConstant: { 911 // Record a code dependency on the cell, and just deoptimize if the new 912 // value doesn't match the previous value stored inside the cell. 913 dependencies()->DependOnGlobalProperty(property_cell); 914 Node* check = 915 graph()->NewNode(simplified()->ReferenceEqual(), value, 916 jsgraph()->Constant(property_cell_value)); 917 effect = graph()->NewNode( 918 simplified()->CheckIf(DeoptimizeReason::kValueMismatch), check, 919 effect, control); 920 break; 921 } 922 case PropertyCellType::kConstantType: { 923 // Record a code dependency on the cell, and just deoptimize if the new 924 // value's type doesn't match the type of the previous value in the 925 // cell. 926 dependencies()->DependOnGlobalProperty(property_cell); 927 Type property_cell_value_type; 928 MachineRepresentation representation = MachineRepresentation::kTagged; 929 if (property_cell_value.IsHeapObject()) { 930 MapRef property_cell_value_map = 931 property_cell_value.AsHeapObject().map(); 932 dependencies()->DependOnStableMap(property_cell_value_map); 933 934 // Check that the {value} is a HeapObject. 935 value = effect = graph()->NewNode(simplified()->CheckHeapObject(), 936 value, effect, control); 937 // Check {value} map against the {property_cell_value} map. 938 effect = graph()->NewNode( 939 simplified()->CheckMaps( 940 CheckMapsFlag::kNone, 941 ZoneHandleSet<Map>(property_cell_value_map.object())), 942 value, effect, control); 943 property_cell_value_type = Type::OtherInternal(); 944 representation = MachineRepresentation::kTaggedPointer; 945 } else { 946 // Check that the {value} is a Smi. 947 value = effect = graph()->NewNode( 948 simplified()->CheckSmi(FeedbackSource()), value, effect, control); 949 property_cell_value_type = Type::SignedSmall(); 950 representation = MachineRepresentation::kTaggedSigned; 951 } 952 effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue( 953 representation, property_cell_value_type, 954 MaybeHandle<Map>(), name)), 955 jsgraph()->Constant(property_cell), value, 956 effect, control); 957 break; 958 } 959 case PropertyCellType::kMutable: { 960 // Record a code dependency on the cell, and just deoptimize if the 961 // property ever becomes read-only. 962 dependencies()->DependOnGlobalProperty(property_cell); 963 effect = graph()->NewNode( 964 simplified()->StoreField(ForPropertyCellValue( 965 MachineRepresentation::kTagged, Type::NonInternal(), 966 MaybeHandle<Map>(), name)), 967 jsgraph()->Constant(property_cell), value, effect, control); 968 break; 969 } 970 case PropertyCellType::kUndefined: 971 case PropertyCellType::kInTransition: 972 UNREACHABLE(); 973 } 974 } 975 976 ReplaceWithValue(node, value, effect, control); 977 return Replace(value); 978} 979 980Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { 981 JSLoadGlobalNode n(node); 982 LoadGlobalParameters const& p = n.Parameters(); 983 if (!p.feedback().IsValid()) return NoChange(); 984 985 ProcessedFeedback const& processed = 986 broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback())); 987 if (processed.IsInsufficient()) return NoChange(); 988 989 GlobalAccessFeedback const& feedback = processed.AsGlobalAccess(); 990 if (feedback.IsScriptContextSlot()) { 991 Effect effect = n.effect(); 992 Node* script_context = jsgraph()->Constant(feedback.script_context()); 993 Node* value = effect = 994 graph()->NewNode(javascript()->LoadContext(0, feedback.slot_index(), 995 feedback.immutable()), 996 script_context, effect); 997 ReplaceWithValue(node, value, effect); 998 return Replace(value); 999 } else if (feedback.IsPropertyCell()) { 1000 return ReduceGlobalAccess(node, nullptr, nullptr, nullptr, p.name(broker()), 1001 AccessMode::kLoad, nullptr, 1002 feedback.property_cell()); 1003 } else { 1004 DCHECK(feedback.IsMegamorphic()); 1005 return NoChange(); 1006 } 1007} 1008 1009Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { 1010 JSStoreGlobalNode n(node); 1011 StoreGlobalParameters const& p = n.Parameters(); 1012 Node* value = n.value(); 1013 if (!p.feedback().IsValid()) return NoChange(); 1014 1015 ProcessedFeedback const& processed = 1016 broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback())); 1017 if (processed.IsInsufficient()) return NoChange(); 1018 1019 GlobalAccessFeedback const& feedback = processed.AsGlobalAccess(); 1020 if (feedback.IsScriptContextSlot()) { 1021 if (feedback.immutable()) return NoChange(); 1022 Effect effect = n.effect(); 1023 Control control = n.control(); 1024 Node* script_context = jsgraph()->Constant(feedback.script_context()); 1025 effect = 1026 graph()->NewNode(javascript()->StoreContext(0, feedback.slot_index()), 1027 value, script_context, effect, control); 1028 ReplaceWithValue(node, value, effect, control); 1029 return Replace(value); 1030 } else if (feedback.IsPropertyCell()) { 1031 return ReduceGlobalAccess(node, nullptr, nullptr, value, p.name(broker()), 1032 AccessMode::kStore, nullptr, 1033 feedback.property_cell()); 1034 } else { 1035 DCHECK(feedback.IsMegamorphic()); 1036 return NoChange(); 1037 } 1038} 1039 1040Reduction JSNativeContextSpecialization::ReduceNamedAccess( 1041 Node* node, Node* value, NamedAccessFeedback const& feedback, 1042 AccessMode access_mode, Node* key) { 1043 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || 1044 node->opcode() == IrOpcode::kJSSetNamedProperty || 1045 node->opcode() == IrOpcode::kJSLoadProperty || 1046 node->opcode() == IrOpcode::kJSSetKeyedProperty || 1047 node->opcode() == IrOpcode::kJSDefineNamedOwnProperty || 1048 node->opcode() == IrOpcode::kJSDefineKeyedOwnPropertyInLiteral || 1049 node->opcode() == IrOpcode::kJSHasProperty || 1050 node->opcode() == IrOpcode::kJSLoadNamedFromSuper || 1051 node->opcode() == IrOpcode::kJSDefineKeyedOwnProperty); 1052 STATIC_ASSERT(JSLoadNamedNode::ObjectIndex() == 0 && 1053 JSSetNamedPropertyNode::ObjectIndex() == 0 && 1054 JSLoadPropertyNode::ObjectIndex() == 0 && 1055 JSSetKeyedPropertyNode::ObjectIndex() == 0 && 1056 JSDefineNamedOwnPropertyNode::ObjectIndex() == 0 && 1057 JSSetNamedPropertyNode::ObjectIndex() == 0 && 1058 JSDefineKeyedOwnPropertyInLiteralNode::ObjectIndex() == 0 && 1059 JSHasPropertyNode::ObjectIndex() == 0 && 1060 JSDefineKeyedOwnPropertyNode::ObjectIndex() == 0); 1061 STATIC_ASSERT(JSLoadNamedFromSuperNode::ReceiverIndex() == 0); 1062 1063 Node* context = NodeProperties::GetContextInput(node); 1064 FrameState frame_state{NodeProperties::GetFrameStateInput(node)}; 1065 Effect effect{NodeProperties::GetEffectInput(node)}; 1066 Control control{NodeProperties::GetControlInput(node)}; 1067 1068 // receiver = the object we pass to the accessor (if any) as the "this" value. 1069 Node* receiver = NodeProperties::GetValueInput(node, 0); 1070 // lookup_start_object = the object where we start looking for the property. 1071 Node* lookup_start_object; 1072 if (node->opcode() == IrOpcode::kJSLoadNamedFromSuper) { 1073 DCHECK(FLAG_super_ic); 1074 JSLoadNamedFromSuperNode n(node); 1075 // Lookup start object is the __proto__ of the home object. 1076 lookup_start_object = effect = 1077 BuildLoadPrototypeFromObject(n.home_object(), effect, control); 1078 } else { 1079 lookup_start_object = receiver; 1080 } 1081 1082 // Either infer maps from the graph or use the feedback. 1083 ZoneVector<MapRef> inferred_maps(zone()); 1084 if (!InferMaps(lookup_start_object, effect, &inferred_maps)) { 1085 for (const MapRef& map : feedback.maps()) { 1086 inferred_maps.push_back(map); 1087 } 1088 } 1089 RemoveImpossibleMaps(lookup_start_object, &inferred_maps); 1090 1091 // Check if we have an access o.x or o.x=v where o is the target native 1092 // contexts' global proxy, and turn that into a direct access to the 1093 // corresponding global object instead. 1094 if (inferred_maps.size() == 1) { 1095 MapRef lookup_start_object_map = inferred_maps[0]; 1096 if (lookup_start_object_map.equals( 1097 native_context().global_proxy_object().map())) { 1098 if (!native_context().GlobalIsDetached()) { 1099 base::Optional<PropertyCellRef> cell = 1100 native_context().global_object().GetPropertyCell(feedback.name()); 1101 if (!cell.has_value()) return NoChange(); 1102 // Note: The map check generated by ReduceGlobalAccesses ensures that we 1103 // will deopt when/if GlobalIsDetached becomes true. 1104 return ReduceGlobalAccess(node, lookup_start_object, receiver, value, 1105 feedback.name(), access_mode, key, *cell, 1106 effect); 1107 } 1108 } 1109 } 1110 1111 ZoneVector<PropertyAccessInfo> access_infos(zone()); 1112 { 1113 ZoneVector<PropertyAccessInfo> access_infos_for_feedback(zone()); 1114 for (const MapRef& map : inferred_maps) { 1115 if (map.is_deprecated()) continue; 1116 1117 // TODO(v8:12547): Support writing to shared structs, which needs a write 1118 // barrier that calls Object::Share to ensure the RHS is shared. 1119 if (InstanceTypeChecker::IsJSSharedStruct(map.instance_type()) && 1120 access_mode == AccessMode::kStore) { 1121 return NoChange(); 1122 } 1123 1124 PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo( 1125 map, feedback.name(), access_mode, dependencies()); 1126 access_infos_for_feedback.push_back(access_info); 1127 } 1128 1129 AccessInfoFactory access_info_factory(broker(), dependencies(), 1130 graph()->zone()); 1131 if (!access_info_factory.FinalizePropertyAccessInfos( 1132 access_infos_for_feedback, access_mode, &access_infos)) { 1133 return NoChange(); 1134 } 1135 } 1136 1137 // Ensure that {key} matches the specified name (if {key} is given). 1138 if (key != nullptr) { 1139 effect = BuildCheckEqualsName(feedback.name(), key, effect, control); 1140 } 1141 1142 // Collect call nodes to rewire exception edges. 1143 ZoneVector<Node*> if_exception_nodes(zone()); 1144 ZoneVector<Node*>* if_exceptions = nullptr; 1145 Node* if_exception = nullptr; 1146 if (NodeProperties::IsExceptionalCall(node, &if_exception)) { 1147 if_exceptions = &if_exception_nodes; 1148 } 1149 1150 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies()); 1151 1152 // Check for the monomorphic cases. 1153 if (access_infos.size() == 1) { 1154 PropertyAccessInfo access_info = access_infos.front(); 1155 if (receiver != lookup_start_object) { 1156 // Super property access. lookup_start_object is a JSReceiver or 1157 // null. It can't be a number, a string etc. So trying to build the 1158 // checks in the "else if" branch doesn't make sense. 1159 access_builder.BuildCheckMaps(lookup_start_object, &effect, control, 1160 access_info.lookup_start_object_maps()); 1161 1162 } else if (!access_builder.TryBuildStringCheck( 1163 broker(), access_info.lookup_start_object_maps(), &receiver, 1164 &effect, control) && 1165 !access_builder.TryBuildNumberCheck( 1166 broker(), access_info.lookup_start_object_maps(), &receiver, 1167 &effect, control)) { 1168 // Try to build string check or number check if possible. Otherwise build 1169 // a map check. 1170 1171 // TryBuildStringCheck and TryBuildNumberCheck don't update the receiver 1172 // if they fail. 1173 DCHECK_EQ(receiver, lookup_start_object); 1174 if (HasNumberMaps(broker(), access_info.lookup_start_object_maps())) { 1175 // We need to also let Smi {receiver}s through in this case, so 1176 // we construct a diamond, guarded by the Sminess of the {receiver} 1177 // and if {receiver} is not a Smi just emit a sequence of map checks. 1178 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); 1179 Node* branch = graph()->NewNode(common()->Branch(), check, control); 1180 1181 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 1182 Node* etrue = effect; 1183 1184 Control if_false{graph()->NewNode(common()->IfFalse(), branch)}; 1185 Effect efalse = effect; 1186 access_builder.BuildCheckMaps(receiver, &efalse, if_false, 1187 access_info.lookup_start_object_maps()); 1188 1189 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 1190 effect = 1191 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 1192 } else { 1193 access_builder.BuildCheckMaps(receiver, &effect, control, 1194 access_info.lookup_start_object_maps()); 1195 } 1196 } else { 1197 // At least one of TryBuildStringCheck & TryBuildNumberCheck succeeded 1198 // and updated the receiver. Update lookup_start_object to match (they 1199 // should be the same). 1200 lookup_start_object = receiver; 1201 } 1202 1203 // Generate the actual property access. 1204 base::Optional<ValueEffectControl> continuation = BuildPropertyAccess( 1205 lookup_start_object, receiver, value, context, frame_state, effect, 1206 control, feedback.name(), if_exceptions, access_info, access_mode); 1207 if (!continuation) { 1208 // At this point we maybe have added nodes into the graph (e.g. via 1209 // NewNode or BuildCheckMaps) in some cases but we haven't connected them 1210 // to End since we haven't called ReplaceWithValue. Since they are nodes 1211 // which are not connected with End, they will be removed by graph 1212 // trimming. 1213 return NoChange(); 1214 } 1215 value = continuation->value(); 1216 effect = continuation->effect(); 1217 control = continuation->control(); 1218 } else { 1219 // The final states for every polymorphic branch. We join them with 1220 // Merge+Phi+EffectPhi at the bottom. 1221 ZoneVector<Node*> values(zone()); 1222 ZoneVector<Node*> effects(zone()); 1223 ZoneVector<Node*> controls(zone()); 1224 1225 Node* receiverissmi_control = nullptr; 1226 Node* receiverissmi_effect = effect; 1227 1228 if (receiver == lookup_start_object) { 1229 // Check if {receiver} may be a number. 1230 bool receiverissmi_possible = false; 1231 for (PropertyAccessInfo const& access_info : access_infos) { 1232 if (HasNumberMaps(broker(), access_info.lookup_start_object_maps())) { 1233 receiverissmi_possible = true; 1234 break; 1235 } 1236 } 1237 1238 // Handle the case that {receiver} may be a number. 1239 if (receiverissmi_possible) { 1240 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); 1241 Node* branch = graph()->NewNode(common()->Branch(), check, control); 1242 control = graph()->NewNode(common()->IfFalse(), branch); 1243 receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch); 1244 receiverissmi_effect = effect; 1245 } 1246 } 1247 1248 // Generate code for the various different property access patterns. 1249 Node* fallthrough_control = control; 1250 for (size_t j = 0; j < access_infos.size(); ++j) { 1251 PropertyAccessInfo const& access_info = access_infos[j]; 1252 Node* this_value = value; 1253 Node* this_lookup_start_object = lookup_start_object; 1254 Node* this_receiver = receiver; 1255 Effect this_effect = effect; 1256 Control this_control{fallthrough_control}; 1257 1258 // Perform map check on {lookup_start_object}. 1259 ZoneVector<MapRef> const& lookup_start_object_maps = 1260 access_info.lookup_start_object_maps(); 1261 { 1262 // Whether to insert a dedicated MapGuard node into the 1263 // effect to be able to learn from the control flow. 1264 bool insert_map_guard = true; 1265 1266 // Check maps for the {lookup_start_object}s. 1267 if (j == access_infos.size() - 1) { 1268 // Last map check on the fallthrough control path, do a 1269 // conditional eager deoptimization exit here. 1270 access_builder.BuildCheckMaps(lookup_start_object, &this_effect, 1271 this_control, lookup_start_object_maps); 1272 fallthrough_control = nullptr; 1273 1274 // Don't insert a MapGuard in this case, as the CheckMaps 1275 // node already gives you all the information you need 1276 // along the effect chain. 1277 insert_map_guard = false; 1278 } else { 1279 // Explicitly branch on the {lookup_start_object_maps}. 1280 ZoneHandleSet<Map> maps; 1281 for (MapRef map : lookup_start_object_maps) { 1282 maps.insert(map.object(), graph()->zone()); 1283 } 1284 Node* check = this_effect = 1285 graph()->NewNode(simplified()->CompareMaps(maps), 1286 lookup_start_object, this_effect, this_control); 1287 Node* branch = 1288 graph()->NewNode(common()->Branch(), check, this_control); 1289 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); 1290 this_control = graph()->NewNode(common()->IfTrue(), branch); 1291 } 1292 1293 // The Number case requires special treatment to also deal with Smis. 1294 if (HasNumberMaps(broker(), lookup_start_object_maps)) { 1295 // Join this check with the "receiver is smi" check above. 1296 DCHECK_EQ(receiver, lookup_start_object); 1297 DCHECK_NOT_NULL(receiverissmi_effect); 1298 DCHECK_NOT_NULL(receiverissmi_control); 1299 this_control = graph()->NewNode(common()->Merge(2), this_control, 1300 receiverissmi_control); 1301 this_effect = graph()->NewNode(common()->EffectPhi(2), this_effect, 1302 receiverissmi_effect, this_control); 1303 receiverissmi_effect = receiverissmi_control = nullptr; 1304 1305 // The {lookup_start_object} can also be a Smi in this case, so 1306 // a MapGuard doesn't make sense for this at all. 1307 insert_map_guard = false; 1308 } 1309 1310 // Introduce a MapGuard to learn from this on the effect chain. 1311 if (insert_map_guard) { 1312 ZoneHandleSet<Map> maps; 1313 for (MapRef map : lookup_start_object_maps) { 1314 maps.insert(map.object(), graph()->zone()); 1315 } 1316 this_effect = 1317 graph()->NewNode(simplified()->MapGuard(maps), 1318 lookup_start_object, this_effect, this_control); 1319 } 1320 1321 // If all {lookup_start_object_maps} are Strings we also need to rename 1322 // the {lookup_start_object} here to make sure that TurboFan knows that 1323 // along this path the {this_lookup_start_object} is a String. This is 1324 // because we want strict checking of types, for example for 1325 // StringLength operators. 1326 if (HasOnlyStringMaps(broker(), lookup_start_object_maps)) { 1327 DCHECK_EQ(receiver, lookup_start_object); 1328 this_lookup_start_object = this_receiver = this_effect = 1329 graph()->NewNode(common()->TypeGuard(Type::String()), 1330 lookup_start_object, this_effect, this_control); 1331 } 1332 } 1333 1334 // Generate the actual property access. 1335 base::Optional<ValueEffectControl> continuation = BuildPropertyAccess( 1336 this_lookup_start_object, this_receiver, this_value, context, 1337 frame_state, this_effect, this_control, feedback.name(), 1338 if_exceptions, access_info, access_mode); 1339 if (!continuation) { 1340 // At this point we maybe have added nodes into the graph (e.g. via 1341 // NewNode or BuildCheckMaps) in some cases but we haven't connected 1342 // them to End since we haven't called ReplaceWithValue. Since they are 1343 // nodes which are not connected with End, they will be removed by graph 1344 // trimming. 1345 return NoChange(); 1346 } 1347 values.push_back(continuation->value()); 1348 effects.push_back(continuation->effect()); 1349 controls.push_back(continuation->control()); 1350 } 1351 1352 DCHECK_NULL(fallthrough_control); 1353 1354 // Generate the final merge point for all (polymorphic) branches. 1355 int const control_count = static_cast<int>(controls.size()); 1356 if (control_count == 0) { 1357 value = effect = control = jsgraph()->Dead(); 1358 } else if (control_count == 1) { 1359 value = values.front(); 1360 effect = effects.front(); 1361 control = controls.front(); 1362 } else { 1363 control = graph()->NewNode(common()->Merge(control_count), control_count, 1364 &controls.front()); 1365 values.push_back(control); 1366 value = graph()->NewNode( 1367 common()->Phi(MachineRepresentation::kTagged, control_count), 1368 control_count + 1, &values.front()); 1369 effects.push_back(control); 1370 effect = graph()->NewNode(common()->EffectPhi(control_count), 1371 control_count + 1, &effects.front()); 1372 } 1373 } 1374 1375 // Properly rewire IfException edges if {node} is inside a try-block. 1376 if (!if_exception_nodes.empty()) { 1377 DCHECK_NOT_NULL(if_exception); 1378 DCHECK_EQ(if_exceptions, &if_exception_nodes); 1379 int const if_exception_count = static_cast<int>(if_exceptions->size()); 1380 Node* merge = graph()->NewNode(common()->Merge(if_exception_count), 1381 if_exception_count, &if_exceptions->front()); 1382 if_exceptions->push_back(merge); 1383 Node* ephi = 1384 graph()->NewNode(common()->EffectPhi(if_exception_count), 1385 if_exception_count + 1, &if_exceptions->front()); 1386 Node* phi = graph()->NewNode( 1387 common()->Phi(MachineRepresentation::kTagged, if_exception_count), 1388 if_exception_count + 1, &if_exceptions->front()); 1389 ReplaceWithValue(if_exception, phi, ephi, merge); 1390 } 1391 1392 ReplaceWithValue(node, value, effect, control); 1393 return Replace(value); 1394} 1395 1396Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { 1397 JSLoadNamedNode n(node); 1398 NamedAccess const& p = n.Parameters(); 1399 Node* const receiver = n.object(); 1400 NameRef name = p.name(broker()); 1401 1402 // Check if we have a constant receiver. 1403 HeapObjectMatcher m(receiver); 1404 if (m.HasResolvedValue()) { 1405 ObjectRef object = m.Ref(broker()); 1406 if (object.IsJSFunction() && 1407 name.equals(MakeRef(broker(), factory()->prototype_string()))) { 1408 // Optimize "prototype" property of functions. 1409 JSFunctionRef function = object.AsJSFunction(); 1410 // TODO(neis): Remove the has_prototype_slot condition once the broker is 1411 // always enabled. 1412 if (!function.map().has_prototype_slot() || 1413 !function.has_instance_prototype(dependencies()) || 1414 function.PrototypeRequiresRuntimeLookup(dependencies())) { 1415 return NoChange(); 1416 } 1417 ObjectRef prototype = dependencies()->DependOnPrototypeProperty(function); 1418 Node* value = jsgraph()->Constant(prototype); 1419 ReplaceWithValue(node, value); 1420 return Replace(value); 1421 } else if (object.IsString() && 1422 name.equals(MakeRef(broker(), factory()->length_string()))) { 1423 // Constant-fold "length" property on constant strings. 1424 if (!object.AsString().length().has_value()) return NoChange(); 1425 Node* value = jsgraph()->Constant(object.AsString().length().value()); 1426 ReplaceWithValue(node, value); 1427 return Replace(value); 1428 } 1429 } 1430 1431 if (!p.feedback().IsValid()) return NoChange(); 1432 return ReducePropertyAccess(node, nullptr, name, jsgraph()->Dead(), 1433 FeedbackSource(p.feedback()), AccessMode::kLoad); 1434} 1435 1436Reduction JSNativeContextSpecialization::ReduceJSLoadNamedFromSuper( 1437 Node* node) { 1438 JSLoadNamedFromSuperNode n(node); 1439 NamedAccess const& p = n.Parameters(); 1440 NameRef name = p.name(broker()); 1441 1442 if (!p.feedback().IsValid()) return NoChange(); 1443 return ReducePropertyAccess(node, nullptr, name, jsgraph()->Dead(), 1444 FeedbackSource(p.feedback()), AccessMode::kLoad); 1445} 1446 1447Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) { 1448 JSGetIteratorNode n(node); 1449 GetIteratorParameters const& p = n.Parameters(); 1450 1451 TNode<Object> receiver = n.receiver(); 1452 TNode<Object> context = n.context(); 1453 FrameState frame_state = n.frame_state(); 1454 Effect effect = n.effect(); 1455 Control control = n.control(); 1456 1457 // Load iterator property operator 1458 NameRef iterator_symbol = MakeRef(broker(), factory()->iterator_symbol()); 1459 const Operator* load_op = 1460 javascript()->LoadNamed(iterator_symbol, p.loadFeedback()); 1461 1462 // Lazy deopt of the load iterator property 1463 // TODO(v8:10047): Use TaggedIndexConstant here once deoptimizer supports it. 1464 Node* call_slot = jsgraph()->SmiConstant(p.callFeedback().slot.ToInt()); 1465 Node* call_feedback = jsgraph()->HeapConstant(p.callFeedback().vector); 1466 Node* lazy_deopt_parameters[] = {receiver, call_slot, call_feedback}; 1467 Node* lazy_deopt_frame_state = CreateStubBuiltinContinuationFrameState( 1468 jsgraph(), Builtin::kGetIteratorWithFeedbackLazyDeoptContinuation, 1469 context, lazy_deopt_parameters, arraysize(lazy_deopt_parameters), 1470 frame_state, ContinuationFrameStateMode::LAZY); 1471 Node* load_property = 1472 graph()->NewNode(load_op, receiver, n.feedback_vector(), context, 1473 lazy_deopt_frame_state, effect, control); 1474 effect = load_property; 1475 control = load_property; 1476 1477 // Handle exception path for the load named property 1478 Node* iterator_exception_node = nullptr; 1479 if (NodeProperties::IsExceptionalCall(node, &iterator_exception_node)) { 1480 // If there exists an exception node for the given iterator_node, create a 1481 // pair of IfException/IfSuccess nodes on the current control path. The uses 1482 // of new exception node are merged with the original exception node. The 1483 // IfSuccess node is returned as a control path for further reduction. 1484 Node* exception_node = 1485 graph()->NewNode(common()->IfException(), effect, control); 1486 Node* if_success = graph()->NewNode(common()->IfSuccess(), control); 1487 1488 // Use dead_node as a placeholder for the original exception node until 1489 // its uses are rewired to the nodes merging the exceptions 1490 Node* dead_node = jsgraph()->Dead(); 1491 Node* merge_node = 1492 graph()->NewNode(common()->Merge(2), dead_node, exception_node); 1493 Node* effect_phi = graph()->NewNode(common()->EffectPhi(2), dead_node, 1494 exception_node, merge_node); 1495 Node* phi = 1496 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 1497 dead_node, exception_node, merge_node); 1498 ReplaceWithValue(iterator_exception_node, phi, effect_phi, merge_node); 1499 phi->ReplaceInput(0, iterator_exception_node); 1500 effect_phi->ReplaceInput(0, iterator_exception_node); 1501 merge_node->ReplaceInput(0, iterator_exception_node); 1502 control = if_success; 1503 } 1504 1505 // Eager deopt of call iterator property 1506 Node* parameters[] = {receiver, load_property, call_slot, call_feedback}; 1507 Node* eager_deopt_frame_state = CreateStubBuiltinContinuationFrameState( 1508 jsgraph(), Builtin::kCallIteratorWithFeedback, context, parameters, 1509 arraysize(parameters), frame_state, ContinuationFrameStateMode::EAGER); 1510 Node* deopt_checkpoint = graph()->NewNode( 1511 common()->Checkpoint(), eager_deopt_frame_state, effect, control); 1512 effect = deopt_checkpoint; 1513 1514 // Call iterator property operator 1515 ProcessedFeedback const& feedback = 1516 broker()->GetFeedbackForCall(p.callFeedback()); 1517 SpeculationMode mode = feedback.IsInsufficient() 1518 ? SpeculationMode::kDisallowSpeculation 1519 : feedback.AsCall().speculation_mode(); 1520 const Operator* call_op = javascript()->Call( 1521 JSCallNode::ArityForArgc(0), CallFrequency(), p.callFeedback(), 1522 ConvertReceiverMode::kNotNullOrUndefined, mode, 1523 CallFeedbackRelation::kTarget); 1524 Node* call_property = 1525 graph()->NewNode(call_op, load_property, receiver, n.feedback_vector(), 1526 context, frame_state, effect, control); 1527 1528 return Replace(call_property); 1529} 1530 1531Reduction JSNativeContextSpecialization::ReduceJSSetNamedProperty(Node* node) { 1532 JSSetNamedPropertyNode n(node); 1533 NamedAccess const& p = n.Parameters(); 1534 if (!p.feedback().IsValid()) return NoChange(); 1535 return ReducePropertyAccess(node, nullptr, p.name(broker()), n.value(), 1536 FeedbackSource(p.feedback()), AccessMode::kStore); 1537} 1538 1539Reduction JSNativeContextSpecialization::ReduceJSDefineNamedOwnProperty( 1540 Node* node) { 1541 JSDefineNamedOwnPropertyNode n(node); 1542 DefineNamedOwnPropertyParameters const& p = n.Parameters(); 1543 if (!p.feedback().IsValid()) return NoChange(); 1544 return ReducePropertyAccess(node, nullptr, p.name(broker()), n.value(), 1545 FeedbackSource(p.feedback()), 1546 AccessMode::kStoreInLiteral); 1547} 1548 1549Reduction JSNativeContextSpecialization::ReduceElementAccessOnString( 1550 Node* node, Node* index, Node* value, KeyedAccessMode const& keyed_mode) { 1551 Node* receiver = NodeProperties::GetValueInput(node, 0); 1552 Node* effect = NodeProperties::GetEffectInput(node); 1553 Node* control = NodeProperties::GetControlInput(node); 1554 1555 // Strings are immutable in JavaScript. 1556 if (keyed_mode.access_mode() == AccessMode::kStore) return NoChange(); 1557 1558 // `in` cannot be used on strings. 1559 if (keyed_mode.access_mode() == AccessMode::kHas) return NoChange(); 1560 1561 // Ensure that the {receiver} is actually a String. 1562 receiver = effect = graph()->NewNode( 1563 simplified()->CheckString(FeedbackSource()), receiver, effect, control); 1564 1565 // Determine the {receiver} length. 1566 Node* length = graph()->NewNode(simplified()->StringLength(), receiver); 1567 1568 // Load the single character string from {receiver} or yield undefined 1569 // if the {index} is out of bounds (depending on the {load_mode}). 1570 value = BuildIndexedStringLoad(receiver, index, length, &effect, &control, 1571 keyed_mode.load_mode()); 1572 1573 ReplaceWithValue(node, value, effect, control); 1574 return Replace(value); 1575} 1576 1577namespace { 1578 1579base::Optional<JSTypedArrayRef> GetTypedArrayConstant(JSHeapBroker* broker, 1580 Node* receiver) { 1581 HeapObjectMatcher m(receiver); 1582 if (!m.HasResolvedValue()) return base::nullopt; 1583 ObjectRef object = m.Ref(broker); 1584 if (!object.IsJSTypedArray()) return base::nullopt; 1585 JSTypedArrayRef typed_array = object.AsJSTypedArray(); 1586 if (typed_array.is_on_heap()) return base::nullopt; 1587 return typed_array; 1588} 1589 1590} // namespace 1591 1592void JSNativeContextSpecialization::RemoveImpossibleMaps( 1593 Node* object, ZoneVector<MapRef>* maps) const { 1594 base::Optional<MapRef> root_map = InferRootMap(object); 1595 if (root_map.has_value() && !root_map->is_abandoned_prototype_map()) { 1596 maps->erase(std::remove_if(maps->begin(), maps->end(), 1597 [root_map](const MapRef& map) { 1598 return map.is_abandoned_prototype_map() || 1599 !map.FindRootMap().equals(*root_map); 1600 }), 1601 maps->end()); 1602 } 1603} 1604 1605// Possibly refine the feedback using inferred map information from the graph. 1606ElementAccessFeedback const& 1607JSNativeContextSpecialization::TryRefineElementAccessFeedback( 1608 ElementAccessFeedback const& feedback, Node* receiver, 1609 Effect effect) const { 1610 AccessMode access_mode = feedback.keyed_mode().access_mode(); 1611 bool use_inference = 1612 access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas; 1613 if (!use_inference) return feedback; 1614 1615 ZoneVector<MapRef> inferred_maps(zone()); 1616 if (!InferMaps(receiver, effect, &inferred_maps)) return feedback; 1617 1618 RemoveImpossibleMaps(receiver, &inferred_maps); 1619 // TODO(neis): After Refine, the resulting feedback can still contain 1620 // impossible maps when a target is kept only because more than one of its 1621 // sources was inferred. Think of a way to completely rule out impossible 1622 // maps. 1623 return feedback.Refine(broker(), inferred_maps); 1624} 1625 1626Reduction JSNativeContextSpecialization::ReduceElementAccess( 1627 Node* node, Node* index, Node* value, 1628 ElementAccessFeedback const& feedback) { 1629 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || 1630 node->opcode() == IrOpcode::kJSSetKeyedProperty || 1631 node->opcode() == IrOpcode::kJSStoreInArrayLiteral || 1632 node->opcode() == IrOpcode::kJSDefineKeyedOwnPropertyInLiteral || 1633 node->opcode() == IrOpcode::kJSHasProperty || 1634 node->opcode() == IrOpcode::kJSDefineKeyedOwnProperty); 1635 STATIC_ASSERT(JSLoadPropertyNode::ObjectIndex() == 0 && 1636 JSSetKeyedPropertyNode::ObjectIndex() == 0 && 1637 JSStoreInArrayLiteralNode::ArrayIndex() == 0 && 1638 JSDefineKeyedOwnPropertyInLiteralNode::ObjectIndex() == 0 && 1639 JSHasPropertyNode::ObjectIndex() == 0); 1640 1641 Node* receiver = NodeProperties::GetValueInput(node, 0); 1642 Effect effect{NodeProperties::GetEffectInput(node)}; 1643 Control control{NodeProperties::GetControlInput(node)}; 1644 1645 // TODO(neis): It's odd that we do optimizations below that don't really care 1646 // about the feedback, but we don't do them when the feedback is megamorphic. 1647 if (feedback.transition_groups().empty()) return NoChange(); 1648 1649 ElementAccessFeedback const& refined_feedback = 1650 TryRefineElementAccessFeedback(feedback, receiver, effect); 1651 1652 AccessMode access_mode = refined_feedback.keyed_mode().access_mode(); 1653 if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) && 1654 receiver->opcode() == IrOpcode::kHeapConstant) { 1655 Reduction reduction = ReduceElementLoadFromHeapConstant( 1656 node, index, access_mode, refined_feedback.keyed_mode().load_mode()); 1657 if (reduction.Changed()) return reduction; 1658 } 1659 1660 if (!refined_feedback.transition_groups().empty() && 1661 refined_feedback.HasOnlyStringMaps(broker())) { 1662 return ReduceElementAccessOnString(node, index, value, 1663 refined_feedback.keyed_mode()); 1664 } 1665 1666 AccessInfoFactory access_info_factory(broker(), dependencies(), 1667 graph()->zone()); 1668 ZoneVector<ElementAccessInfo> access_infos(zone()); 1669 if (!access_info_factory.ComputeElementAccessInfos(refined_feedback, 1670 &access_infos) || 1671 access_infos.empty()) { 1672 return NoChange(); 1673 } 1674 1675 // For holey stores or growing stores, we need to check that the prototype 1676 // chain contains no setters for elements, and we need to guard those checks 1677 // via code dependencies on the relevant prototype maps. 1678 if (access_mode == AccessMode::kStore) { 1679 // TODO(turbofan): We could have a fast path here, that checks for the 1680 // common case of Array or Object prototype only and therefore avoids 1681 // the zone allocation of this vector. 1682 ZoneVector<MapRef> prototype_maps(zone()); 1683 for (ElementAccessInfo const& access_info : access_infos) { 1684 for (MapRef receiver_map : access_info.lookup_start_object_maps()) { 1685 // If the {receiver_map} has a prototype and its elements backing 1686 // store is either holey, or we have a potentially growing store, 1687 // then we need to check that all prototypes have stable maps with 1688 // fast elements (and we need to guard against changes to that below). 1689 if ((IsHoleyOrDictionaryElementsKind(receiver_map.elements_kind()) || 1690 IsGrowStoreMode(feedback.keyed_mode().store_mode())) && 1691 !receiver_map.HasOnlyStablePrototypesWithFastElements( 1692 &prototype_maps)) { 1693 return NoChange(); 1694 } 1695 1696 // TODO(v8:12547): Support writing to shared structs, which needs a 1697 // write barrier that calls Object::Share to ensure the RHS is shared. 1698 if (InstanceTypeChecker::IsJSSharedStruct( 1699 receiver_map.instance_type())) { 1700 return NoChange(); 1701 } 1702 } 1703 } 1704 for (MapRef const& prototype_map : prototype_maps) { 1705 dependencies()->DependOnStableMap(prototype_map); 1706 } 1707 } else if (access_mode == AccessMode::kHas) { 1708 // If we have any fast arrays, we need to check and depend on 1709 // NoElementsProtector. 1710 for (ElementAccessInfo const& access_info : access_infos) { 1711 if (IsFastElementsKind(access_info.elements_kind())) { 1712 if (!dependencies()->DependOnNoElementsProtector()) return NoChange(); 1713 break; 1714 } 1715 } 1716 } 1717 1718 // Check for the monomorphic case. 1719 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies()); 1720 if (access_infos.size() == 1) { 1721 ElementAccessInfo access_info = access_infos.front(); 1722 1723 // Perform possible elements kind transitions. 1724 MapRef transition_target = access_info.lookup_start_object_maps().front(); 1725 for (MapRef transition_source : access_info.transition_sources()) { 1726 DCHECK_EQ(access_info.lookup_start_object_maps().size(), 1); 1727 effect = graph()->NewNode( 1728 simplified()->TransitionElementsKind(ElementsTransition( 1729 IsSimpleMapChangeTransition(transition_source.elements_kind(), 1730 transition_target.elements_kind()) 1731 ? ElementsTransition::kFastTransition 1732 : ElementsTransition::kSlowTransition, 1733 transition_source.object(), transition_target.object())), 1734 receiver, effect, control); 1735 } 1736 1737 // TODO(turbofan): The effect/control linearization will not find a 1738 // FrameState after the StoreField or Call that is generated for the 1739 // elements kind transition above. This is because those operators 1740 // don't have the kNoWrite flag on it, even though they are not 1741 // observable by JavaScript. 1742 Node* frame_state = 1743 NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead()); 1744 effect = 1745 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); 1746 1747 // Perform map check on the {receiver}. 1748 access_builder.BuildCheckMaps(receiver, &effect, control, 1749 access_info.lookup_start_object_maps()); 1750 1751 // Access the actual element. 1752 ValueEffectControl continuation = 1753 BuildElementAccess(receiver, index, value, effect, control, access_info, 1754 feedback.keyed_mode()); 1755 value = continuation.value(); 1756 effect = continuation.effect(); 1757 control = continuation.control(); 1758 } else { 1759 // The final states for every polymorphic branch. We join them with 1760 // Merge+Phi+EffectPhi at the bottom. 1761 ZoneVector<Node*> values(zone()); 1762 ZoneVector<Node*> effects(zone()); 1763 ZoneVector<Node*> controls(zone()); 1764 1765 // Generate code for the various different element access patterns. 1766 Node* fallthrough_control = control; 1767 for (size_t j = 0; j < access_infos.size(); ++j) { 1768 ElementAccessInfo const& access_info = access_infos[j]; 1769 Node* this_receiver = receiver; 1770 Node* this_value = value; 1771 Node* this_index = index; 1772 Effect this_effect = effect; 1773 Control this_control{fallthrough_control}; 1774 1775 // Perform possible elements kind transitions. 1776 MapRef transition_target = access_info.lookup_start_object_maps().front(); 1777 for (MapRef transition_source : access_info.transition_sources()) { 1778 DCHECK_EQ(access_info.lookup_start_object_maps().size(), 1); 1779 this_effect = graph()->NewNode( 1780 simplified()->TransitionElementsKind(ElementsTransition( 1781 IsSimpleMapChangeTransition(transition_source.elements_kind(), 1782 transition_target.elements_kind()) 1783 ? ElementsTransition::kFastTransition 1784 : ElementsTransition::kSlowTransition, 1785 transition_source.object(), transition_target.object())), 1786 receiver, this_effect, this_control); 1787 } 1788 1789 // Perform map check(s) on {receiver}. 1790 ZoneVector<MapRef> const& receiver_maps = 1791 access_info.lookup_start_object_maps(); 1792 if (j == access_infos.size() - 1) { 1793 // Last map check on the fallthrough control path, do a 1794 // conditional eager deoptimization exit here. 1795 access_builder.BuildCheckMaps(receiver, &this_effect, this_control, 1796 receiver_maps); 1797 fallthrough_control = nullptr; 1798 } else { 1799 // Explicitly branch on the {receiver_maps}. 1800 ZoneHandleSet<Map> maps; 1801 for (MapRef map : receiver_maps) { 1802 maps.insert(map.object(), graph()->zone()); 1803 } 1804 Node* check = this_effect = 1805 graph()->NewNode(simplified()->CompareMaps(maps), receiver, 1806 this_effect, fallthrough_control); 1807 Node* branch = 1808 graph()->NewNode(common()->Branch(), check, fallthrough_control); 1809 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); 1810 this_control = graph()->NewNode(common()->IfTrue(), branch); 1811 1812 // Introduce a MapGuard to learn from this on the effect chain. 1813 this_effect = graph()->NewNode(simplified()->MapGuard(maps), receiver, 1814 this_effect, this_control); 1815 } 1816 1817 // Access the actual element. 1818 ValueEffectControl continuation = 1819 BuildElementAccess(this_receiver, this_index, this_value, this_effect, 1820 this_control, access_info, feedback.keyed_mode()); 1821 values.push_back(continuation.value()); 1822 effects.push_back(continuation.effect()); 1823 controls.push_back(continuation.control()); 1824 } 1825 1826 DCHECK_NULL(fallthrough_control); 1827 1828 // Generate the final merge point for all (polymorphic) branches. 1829 int const control_count = static_cast<int>(controls.size()); 1830 if (control_count == 0) { 1831 value = effect = control = jsgraph()->Dead(); 1832 } else if (control_count == 1) { 1833 value = values.front(); 1834 effect = effects.front(); 1835 control = controls.front(); 1836 } else { 1837 control = graph()->NewNode(common()->Merge(control_count), control_count, 1838 &controls.front()); 1839 values.push_back(control); 1840 value = graph()->NewNode( 1841 common()->Phi(MachineRepresentation::kTagged, control_count), 1842 control_count + 1, &values.front()); 1843 effects.push_back(control); 1844 effect = graph()->NewNode(common()->EffectPhi(control_count), 1845 control_count + 1, &effects.front()); 1846 } 1847 } 1848 1849 ReplaceWithValue(node, value, effect, control); 1850 return Replace(value); 1851} 1852 1853Reduction JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant( 1854 Node* node, Node* key, AccessMode access_mode, 1855 KeyedAccessLoadMode load_mode) { 1856 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || 1857 node->opcode() == IrOpcode::kJSHasProperty); 1858 Node* receiver = NodeProperties::GetValueInput(node, 0); 1859 Node* effect = NodeProperties::GetEffectInput(node); 1860 Node* control = NodeProperties::GetControlInput(node); 1861 1862 HeapObjectMatcher mreceiver(receiver); 1863 HeapObjectRef receiver_ref = mreceiver.Ref(broker()); 1864 if (receiver_ref.map().oddball_type() == OddballType::kHole || 1865 receiver_ref.map().oddball_type() == OddballType::kNull || 1866 receiver_ref.map().oddball_type() == OddballType::kUndefined || 1867 // The 'in' operator throws a TypeError on primitive values. 1868 (receiver_ref.IsString() && access_mode == AccessMode::kHas)) { 1869 return NoChange(); 1870 } 1871 1872 // Check whether we're accessing a known element on the {receiver} and can 1873 // constant-fold the load. 1874 NumberMatcher mkey(key); 1875 if (mkey.IsInteger() && 1876 mkey.IsInRange(0.0, static_cast<double>(JSObject::kMaxElementIndex))) { 1877 STATIC_ASSERT(JSObject::kMaxElementIndex <= kMaxUInt32); 1878 const uint32_t index = static_cast<uint32_t>(mkey.ResolvedValue()); 1879 base::Optional<ObjectRef> element; 1880 1881 if (receiver_ref.IsJSObject()) { 1882 JSObjectRef jsobject_ref = receiver_ref.AsJSObject(); 1883 base::Optional<FixedArrayBaseRef> elements = 1884 jsobject_ref.elements(kRelaxedLoad); 1885 if (elements.has_value()) { 1886 element = jsobject_ref.GetOwnConstantElement(*elements, index, 1887 dependencies()); 1888 if (!element.has_value() && receiver_ref.IsJSArray()) { 1889 // We didn't find a constant element, but if the receiver is a 1890 // cow-array we can exploit the fact that any future write to the 1891 // element will replace the whole elements storage. 1892 element = receiver_ref.AsJSArray().GetOwnCowElement(*elements, index); 1893 if (element.has_value()) { 1894 Node* actual_elements = effect = graph()->NewNode( 1895 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), 1896 receiver, effect, control); 1897 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), 1898 actual_elements, 1899 jsgraph()->Constant(*elements)); 1900 effect = graph()->NewNode( 1901 simplified()->CheckIf( 1902 DeoptimizeReason::kCowArrayElementsChanged), 1903 check, effect, control); 1904 } 1905 } 1906 } 1907 } else if (receiver_ref.IsString()) { 1908 element = receiver_ref.AsString().GetCharAsStringOrUndefined(index); 1909 } 1910 1911 if (element.has_value()) { 1912 Node* value = access_mode == AccessMode::kHas 1913 ? jsgraph()->TrueConstant() 1914 : jsgraph()->Constant(*element); 1915 ReplaceWithValue(node, value, effect, control); 1916 return Replace(value); 1917 } 1918 } 1919 1920 // For constant Strings we can eagerly strength-reduce the keyed 1921 // accesses using the known length, which doesn't change. 1922 if (receiver_ref.IsString()) { 1923 DCHECK_NE(access_mode, AccessMode::kHas); 1924 // Ensure that {key} is less than {receiver} length. 1925 if (!receiver_ref.AsString().length().has_value()) return NoChange(); 1926 Node* length = 1927 jsgraph()->Constant(receiver_ref.AsString().length().value()); 1928 1929 // Load the single character string from {receiver} or yield 1930 // undefined if the {key} is out of bounds (depending on the 1931 // {load_mode}). 1932 Node* value = BuildIndexedStringLoad(receiver, key, length, &effect, 1933 &control, load_mode); 1934 ReplaceWithValue(node, value, effect, control); 1935 return Replace(value); 1936 } 1937 1938 return NoChange(); 1939} 1940 1941Reduction JSNativeContextSpecialization::ReducePropertyAccess( 1942 Node* node, Node* key, base::Optional<NameRef> static_name, Node* value, 1943 FeedbackSource const& source, AccessMode access_mode) { 1944 DCHECK_EQ(key == nullptr, static_name.has_value()); 1945 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || 1946 node->opcode() == IrOpcode::kJSSetKeyedProperty || 1947 node->opcode() == IrOpcode::kJSStoreInArrayLiteral || 1948 node->opcode() == IrOpcode::kJSDefineKeyedOwnPropertyInLiteral || 1949 node->opcode() == IrOpcode::kJSHasProperty || 1950 node->opcode() == IrOpcode::kJSLoadNamed || 1951 node->opcode() == IrOpcode::kJSSetNamedProperty || 1952 node->opcode() == IrOpcode::kJSDefineNamedOwnProperty || 1953 node->opcode() == IrOpcode::kJSLoadNamedFromSuper || 1954 node->opcode() == IrOpcode::kJSDefineKeyedOwnProperty); 1955 DCHECK_GE(node->op()->ControlOutputCount(), 1); 1956 1957 ProcessedFeedback const& feedback = 1958 broker()->GetFeedbackForPropertyAccess(source, access_mode, static_name); 1959 switch (feedback.kind()) { 1960 case ProcessedFeedback::kInsufficient: 1961 return ReduceEagerDeoptimize( 1962 node, 1963 DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess); 1964 case ProcessedFeedback::kNamedAccess: 1965 return ReduceNamedAccess(node, value, feedback.AsNamedAccess(), 1966 access_mode, key); 1967 case ProcessedFeedback::kElementAccess: 1968 DCHECK_EQ(feedback.AsElementAccess().keyed_mode().access_mode(), 1969 access_mode); 1970 DCHECK_NE(node->opcode(), IrOpcode::kJSLoadNamedFromSuper); 1971 return ReduceElementAccess(node, key, value, feedback.AsElementAccess()); 1972 default: 1973 UNREACHABLE(); 1974 } 1975} 1976 1977Reduction JSNativeContextSpecialization::ReduceEagerDeoptimize( 1978 Node* node, DeoptimizeReason reason) { 1979 if (!(flags() & kBailoutOnUninitialized)) return NoChange(); 1980 1981 Node* effect = NodeProperties::GetEffectInput(node); 1982 Node* control = NodeProperties::GetControlInput(node); 1983 Node* frame_state = 1984 NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead()); 1985 Node* deoptimize = 1986 graph()->NewNode(common()->Deoptimize(reason, FeedbackSource()), 1987 frame_state, effect, control); 1988 // TODO(bmeurer): This should be on the AdvancedReducer somehow. 1989 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); 1990 Revisit(graph()->end()); 1991 node->TrimInputCount(0); 1992 NodeProperties::ChangeOp(node, common()->Dead()); 1993 return Changed(node); 1994} 1995 1996Reduction JSNativeContextSpecialization::ReduceJSHasProperty(Node* node) { 1997 JSHasPropertyNode n(node); 1998 PropertyAccess const& p = n.Parameters(); 1999 Node* value = jsgraph()->Dead(); 2000 if (!p.feedback().IsValid()) return NoChange(); 2001 return ReducePropertyAccess(node, n.key(), base::nullopt, value, 2002 FeedbackSource(p.feedback()), AccessMode::kHas); 2003} 2004 2005Reduction JSNativeContextSpecialization::ReduceJSLoadPropertyWithEnumeratedKey( 2006 Node* node) { 2007 // We can optimize a property load if it's being used inside a for..in: 2008 // for (name in receiver) { 2009 // value = receiver[name]; 2010 // ... 2011 // } 2012 // 2013 // If the for..in is in fast-mode, we know that the {receiver} has {name} 2014 // as own property, otherwise the enumeration wouldn't include it. The graph 2015 // constructed by the BytecodeGraphBuilder in this case looks like this: 2016 2017 // receiver 2018 // ^ ^ 2019 // | | 2020 // | +-+ 2021 // | | 2022 // | JSToObject 2023 // | ^ 2024 // | | 2025 // | | 2026 // | JSForInNext 2027 // | ^ 2028 // | | 2029 // +----+ | 2030 // | | 2031 // | | 2032 // JSLoadProperty 2033 2034 // If the for..in has only seen maps with enum cache consisting of keys 2035 // and indices so far, we can turn the {JSLoadProperty} into a map check 2036 // on the {receiver} and then just load the field value dynamically via 2037 // the {LoadFieldByIndex} operator. The map check is only necessary when 2038 // TurboFan cannot prove that there is no observable side effect between 2039 // the {JSForInNext} and the {JSLoadProperty} node. 2040 // 2041 // Also note that it's safe to look through the {JSToObject}, since the 2042 // [[Get]] operation does an implicit ToObject anyway, and these operations 2043 // are not observable. 2044 2045 DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode()); 2046 Node* receiver = NodeProperties::GetValueInput(node, 0); 2047 JSForInNextNode name(NodeProperties::GetValueInput(node, 1)); 2048 Node* effect = NodeProperties::GetEffectInput(node); 2049 Node* control = NodeProperties::GetControlInput(node); 2050 2051 if (name.Parameters().mode() != ForInMode::kUseEnumCacheKeysAndIndices) { 2052 return NoChange(); 2053 } 2054 2055 Node* object = name.receiver(); 2056 Node* cache_type = name.cache_type(); 2057 Node* index = name.index(); 2058 if (object->opcode() == IrOpcode::kJSToObject) { 2059 object = NodeProperties::GetValueInput(object, 0); 2060 } 2061 if (object != receiver) return NoChange(); 2062 2063 // No need to repeat the map check if we can prove that there's no 2064 // observable side effect between {effect} and {name]. 2065 if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) { 2066 // Check that the {receiver} map is still valid. 2067 Node* receiver_map = effect = 2068 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), 2069 receiver, effect, control); 2070 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), receiver_map, 2071 cache_type); 2072 effect = 2073 graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongMap), 2074 check, effect, control); 2075 } 2076 2077 // Load the enum cache indices from the {cache_type}. 2078 Node* descriptor_array = effect = graph()->NewNode( 2079 simplified()->LoadField(AccessBuilder::ForMapDescriptors()), cache_type, 2080 effect, control); 2081 Node* enum_cache = effect = graph()->NewNode( 2082 simplified()->LoadField(AccessBuilder::ForDescriptorArrayEnumCache()), 2083 descriptor_array, effect, control); 2084 Node* enum_indices = effect = graph()->NewNode( 2085 simplified()->LoadField(AccessBuilder::ForEnumCacheIndices()), enum_cache, 2086 effect, control); 2087 2088 // Ensure that the {enum_indices} are valid. 2089 Node* check = graph()->NewNode( 2090 simplified()->BooleanNot(), 2091 graph()->NewNode(simplified()->ReferenceEqual(), enum_indices, 2092 jsgraph()->EmptyFixedArrayConstant())); 2093 effect = graph()->NewNode( 2094 simplified()->CheckIf(DeoptimizeReason::kWrongEnumIndices), check, effect, 2095 control); 2096 2097 // Determine the key from the {enum_indices}. 2098 Node* key = effect = graph()->NewNode( 2099 simplified()->LoadElement( 2100 AccessBuilder::ForFixedArrayElement(PACKED_SMI_ELEMENTS)), 2101 enum_indices, index, effect, control); 2102 2103 // Load the actual field value. 2104 Node* value = effect = graph()->NewNode(simplified()->LoadFieldByIndex(), 2105 receiver, key, effect, control); 2106 ReplaceWithValue(node, value, effect, control); 2107 return Replace(value); 2108} 2109 2110Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) { 2111 JSLoadPropertyNode n(node); 2112 PropertyAccess const& p = n.Parameters(); 2113 Node* name = n.key(); 2114 2115 if (name->opcode() == IrOpcode::kJSForInNext) { 2116 Reduction reduction = ReduceJSLoadPropertyWithEnumeratedKey(node); 2117 if (reduction.Changed()) return reduction; 2118 } 2119 2120 if (!p.feedback().IsValid()) return NoChange(); 2121 Node* value = jsgraph()->Dead(); 2122 return ReducePropertyAccess(node, name, base::nullopt, value, 2123 FeedbackSource(p.feedback()), AccessMode::kLoad); 2124} 2125 2126Reduction JSNativeContextSpecialization::ReduceJSSetKeyedProperty(Node* node) { 2127 JSSetKeyedPropertyNode n(node); 2128 PropertyAccess const& p = n.Parameters(); 2129 if (!p.feedback().IsValid()) return NoChange(); 2130 return ReducePropertyAccess(node, n.key(), base::nullopt, n.value(), 2131 FeedbackSource(p.feedback()), AccessMode::kStore); 2132} 2133 2134Reduction JSNativeContextSpecialization::ReduceJSDefineKeyedOwnProperty( 2135 Node* node) { 2136 JSDefineKeyedOwnPropertyNode n(node); 2137 PropertyAccess const& p = n.Parameters(); 2138 if (!p.feedback().IsValid()) return NoChange(); 2139 return ReducePropertyAccess(node, n.key(), base::nullopt, n.value(), 2140 FeedbackSource(p.feedback()), 2141 AccessMode::kDefine); 2142} 2143 2144Node* JSNativeContextSpecialization::InlinePropertyGetterCall( 2145 Node* receiver, ConvertReceiverMode receiver_mode, 2146 Node* lookup_start_object, Node* context, Node* frame_state, Node** effect, 2147 Node** control, ZoneVector<Node*>* if_exceptions, 2148 PropertyAccessInfo const& access_info) { 2149 ObjectRef constant = access_info.constant().value(); 2150 2151 if (access_info.IsDictionaryProtoAccessorConstant()) { 2152 // For fast mode holders we recorded dependencies in BuildPropertyLoad. 2153 for (const MapRef map : access_info.lookup_start_object_maps()) { 2154 dependencies()->DependOnConstantInDictionaryPrototypeChain( 2155 map, access_info.name(), constant, PropertyKind::kAccessor); 2156 } 2157 } 2158 2159 Node* target = jsgraph()->Constant(constant); 2160 // Introduce the call to the getter function. 2161 Node* value; 2162 if (constant.IsJSFunction()) { 2163 Node* feedback = jsgraph()->UndefinedConstant(); 2164 value = *effect = *control = graph()->NewNode( 2165 jsgraph()->javascript()->Call(JSCallNode::ArityForArgc(0), 2166 CallFrequency(), FeedbackSource(), 2167 receiver_mode), 2168 target, receiver, feedback, context, frame_state, *effect, *control); 2169 } else { 2170 // Disable optimizations for super ICs using API getters, so that we get 2171 // the correct receiver checks. 2172 if (receiver != lookup_start_object) { 2173 return nullptr; 2174 } 2175 Node* holder = access_info.holder().has_value() 2176 ? jsgraph()->Constant(access_info.holder().value()) 2177 : receiver; 2178 value = InlineApiCall(receiver, holder, frame_state, nullptr, effect, 2179 control, constant.AsFunctionTemplateInfo()); 2180 } 2181 // Remember to rewire the IfException edge if this is inside a try-block. 2182 if (if_exceptions != nullptr) { 2183 // Create the appropriate IfException/IfSuccess projections. 2184 Node* const if_exception = 2185 graph()->NewNode(common()->IfException(), *control, *effect); 2186 Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control); 2187 if_exceptions->push_back(if_exception); 2188 *control = if_success; 2189 } 2190 return value; 2191} 2192 2193void JSNativeContextSpecialization::InlinePropertySetterCall( 2194 Node* receiver, Node* value, Node* context, Node* frame_state, 2195 Node** effect, Node** control, ZoneVector<Node*>* if_exceptions, 2196 PropertyAccessInfo const& access_info) { 2197 ObjectRef constant = access_info.constant().value(); 2198 Node* target = jsgraph()->Constant(constant); 2199 // Introduce the call to the setter function. 2200 if (constant.IsJSFunction()) { 2201 Node* feedback = jsgraph()->UndefinedConstant(); 2202 *effect = *control = graph()->NewNode( 2203 jsgraph()->javascript()->Call(JSCallNode::ArityForArgc(1), 2204 CallFrequency(), FeedbackSource(), 2205 ConvertReceiverMode::kNotNullOrUndefined), 2206 target, receiver, value, feedback, context, frame_state, *effect, 2207 *control); 2208 } else { 2209 Node* holder = access_info.holder().has_value() 2210 ? jsgraph()->Constant(access_info.holder().value()) 2211 : receiver; 2212 InlineApiCall(receiver, holder, frame_state, value, effect, control, 2213 constant.AsFunctionTemplateInfo()); 2214 } 2215 // Remember to rewire the IfException edge if this is inside a try-block. 2216 if (if_exceptions != nullptr) { 2217 // Create the appropriate IfException/IfSuccess projections. 2218 Node* const if_exception = 2219 graph()->NewNode(common()->IfException(), *control, *effect); 2220 Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control); 2221 if_exceptions->push_back(if_exception); 2222 *control = if_success; 2223 } 2224} 2225 2226Node* JSNativeContextSpecialization::InlineApiCall( 2227 Node* receiver, Node* holder, Node* frame_state, Node* value, Node** effect, 2228 Node** control, FunctionTemplateInfoRef const& function_template_info) { 2229 if (!function_template_info.call_code().has_value()) { 2230 TRACE_BROKER_MISSING(broker(), "call code for function template info " 2231 << function_template_info); 2232 return nullptr; 2233 } 2234 CallHandlerInfoRef call_handler_info = *function_template_info.call_code(); 2235 2236 // Only setters have a value. 2237 int const argc = value == nullptr ? 0 : 1; 2238 // The stub always expects the receiver as the first param on the stack. 2239 Callable call_api_callback = CodeFactory::CallApiCallback(isolate()); 2240 CallInterfaceDescriptor call_interface_descriptor = 2241 call_api_callback.descriptor(); 2242 auto call_descriptor = Linkage::GetStubCallDescriptor( 2243 graph()->zone(), call_interface_descriptor, 2244 call_interface_descriptor.GetStackParameterCount() + argc + 2245 1 /* implicit receiver */, 2246 CallDescriptor::kNeedsFrameState); 2247 2248 Node* data = jsgraph()->Constant(call_handler_info.data()); 2249 ApiFunction function(call_handler_info.callback()); 2250 Node* function_reference = 2251 graph()->NewNode(common()->ExternalConstant(ExternalReference::Create( 2252 &function, ExternalReference::DIRECT_API_CALL))); 2253 Node* code = jsgraph()->HeapConstant(call_api_callback.code()); 2254 2255 // Add CallApiCallbackStub's register argument as well. 2256 Node* context = jsgraph()->Constant(native_context()); 2257 Node* inputs[11] = { 2258 code, function_reference, jsgraph()->Constant(argc), data, holder, 2259 receiver}; 2260 int index = 6 + argc; 2261 inputs[index++] = context; 2262 inputs[index++] = frame_state; 2263 inputs[index++] = *effect; 2264 inputs[index++] = *control; 2265 // This needs to stay here because of the edge case described in 2266 // http://crbug.com/675648. 2267 if (value != nullptr) { 2268 inputs[6] = value; 2269 } 2270 2271 return *effect = *control = 2272 graph()->NewNode(common()->Call(call_descriptor), index, inputs); 2273} 2274 2275base::Optional<JSNativeContextSpecialization::ValueEffectControl> 2276JSNativeContextSpecialization::BuildPropertyLoad( 2277 Node* lookup_start_object, Node* receiver, Node* context, Node* frame_state, 2278 Node* effect, Node* control, NameRef const& name, 2279 ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info) { 2280 // Determine actual holder and perform prototype chain checks. 2281 base::Optional<JSObjectRef> holder = access_info.holder(); 2282 if (holder.has_value() && !access_info.HasDictionaryHolder()) { 2283 dependencies()->DependOnStablePrototypeChains( 2284 access_info.lookup_start_object_maps(), kStartAtPrototype, 2285 holder.value()); 2286 } 2287 2288 // Generate the actual property access. 2289 Node* value; 2290 if (access_info.IsNotFound()) { 2291 value = jsgraph()->UndefinedConstant(); 2292 } else if (access_info.IsFastAccessorConstant() || 2293 access_info.IsDictionaryProtoAccessorConstant()) { 2294 ConvertReceiverMode receiver_mode = 2295 receiver == lookup_start_object 2296 ? ConvertReceiverMode::kNotNullOrUndefined 2297 : ConvertReceiverMode::kAny; 2298 value = InlinePropertyGetterCall( 2299 receiver, receiver_mode, lookup_start_object, context, frame_state, 2300 &effect, &control, if_exceptions, access_info); 2301 } else if (access_info.IsModuleExport()) { 2302 Node* cell = jsgraph()->Constant(access_info.constant().value().AsCell()); 2303 value = effect = 2304 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()), 2305 cell, effect, control); 2306 } else if (access_info.IsStringLength()) { 2307 DCHECK_EQ(receiver, lookup_start_object); 2308 value = graph()->NewNode(simplified()->StringLength(), receiver); 2309 } else { 2310 DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant() || 2311 access_info.IsDictionaryProtoDataConstant()); 2312 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies()); 2313 if (access_info.IsDictionaryProtoDataConstant()) { 2314 auto maybe_value = 2315 access_builder.FoldLoadDictPrototypeConstant(access_info); 2316 if (!maybe_value) return {}; 2317 value = maybe_value.value(); 2318 } else { 2319 value = access_builder.BuildLoadDataField( 2320 name, access_info, lookup_start_object, &effect, &control); 2321 } 2322 } 2323 if (value != nullptr) { 2324 return ValueEffectControl(value, effect, control); 2325 } 2326 return base::Optional<ValueEffectControl>(); 2327} 2328 2329JSNativeContextSpecialization::ValueEffectControl 2330JSNativeContextSpecialization::BuildPropertyTest( 2331 Node* effect, Node* control, PropertyAccessInfo const& access_info) { 2332 // TODO(v8:11457) Support property tests for dictionary mode protoypes. 2333 DCHECK(!access_info.HasDictionaryHolder()); 2334 2335 // Determine actual holder and perform prototype chain checks. 2336 base::Optional<JSObjectRef> holder = access_info.holder(); 2337 if (holder.has_value()) { 2338 dependencies()->DependOnStablePrototypeChains( 2339 access_info.lookup_start_object_maps(), kStartAtPrototype, 2340 holder.value()); 2341 } 2342 2343 Node* value = access_info.IsNotFound() ? jsgraph()->FalseConstant() 2344 : jsgraph()->TrueConstant(); 2345 return ValueEffectControl(value, effect, control); 2346} 2347 2348base::Optional<JSNativeContextSpecialization::ValueEffectControl> 2349JSNativeContextSpecialization::BuildPropertyAccess( 2350 Node* lookup_start_object, Node* receiver, Node* value, Node* context, 2351 Node* frame_state, Node* effect, Node* control, NameRef const& name, 2352 ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info, 2353 AccessMode access_mode) { 2354 switch (access_mode) { 2355 case AccessMode::kLoad: 2356 return BuildPropertyLoad(lookup_start_object, receiver, context, 2357 frame_state, effect, control, name, 2358 if_exceptions, access_info); 2359 case AccessMode::kStore: 2360 case AccessMode::kStoreInLiteral: 2361 case AccessMode::kDefine: 2362 DCHECK_EQ(receiver, lookup_start_object); 2363 return BuildPropertyStore(receiver, value, context, frame_state, effect, 2364 control, name, if_exceptions, access_info, 2365 access_mode); 2366 case AccessMode::kHas: 2367 DCHECK_EQ(receiver, lookup_start_object); 2368 return BuildPropertyTest(effect, control, access_info); 2369 } 2370 UNREACHABLE(); 2371} 2372 2373JSNativeContextSpecialization::ValueEffectControl 2374JSNativeContextSpecialization::BuildPropertyStore( 2375 Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect, 2376 Node* control, NameRef const& name, ZoneVector<Node*>* if_exceptions, 2377 PropertyAccessInfo const& access_info, AccessMode access_mode) { 2378 // Determine actual holder and perform prototype chain checks. 2379 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies()); 2380 base::Optional<JSObjectRef> holder = access_info.holder(); 2381 if (holder.has_value()) { 2382 DCHECK_NE(AccessMode::kStoreInLiteral, access_mode); 2383 DCHECK_NE(AccessMode::kDefine, access_mode); 2384 dependencies()->DependOnStablePrototypeChains( 2385 access_info.lookup_start_object_maps(), kStartAtPrototype, 2386 holder.value()); 2387 } 2388 2389 DCHECK(!access_info.IsNotFound()); 2390 2391 // Generate the actual property access. 2392 if (access_info.IsFastAccessorConstant()) { 2393 InlinePropertySetterCall(receiver, value, context, frame_state, &effect, 2394 &control, if_exceptions, access_info); 2395 } else { 2396 DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant()); 2397 DCHECK(access_mode == AccessMode::kStore || 2398 access_mode == AccessMode::kStoreInLiteral || 2399 access_mode == AccessMode::kDefine); 2400 FieldIndex const field_index = access_info.field_index(); 2401 Type const field_type = access_info.field_type(); 2402 MachineRepresentation const field_representation = 2403 PropertyAccessBuilder::ConvertRepresentation( 2404 access_info.field_representation()); 2405 Node* storage = receiver; 2406 if (!field_index.is_inobject()) { 2407 storage = effect = graph()->NewNode( 2408 simplified()->LoadField( 2409 AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()), 2410 storage, effect, control); 2411 } 2412 bool store_to_existing_constant_field = access_info.IsFastDataConstant() && 2413 access_mode == AccessMode::kStore && 2414 !access_info.HasTransitionMap(); 2415 FieldAccess field_access = { 2416 kTaggedBase, 2417 field_index.offset(), 2418 name.object(), 2419 MaybeHandle<Map>(), 2420 field_type, 2421 MachineType::TypeForRepresentation(field_representation), 2422 kFullWriteBarrier, 2423 access_info.GetConstFieldInfo(), 2424 access_mode == AccessMode::kStoreInLiteral}; 2425 2426 switch (field_representation) { 2427 case MachineRepresentation::kFloat64: { 2428 value = effect = 2429 graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), value, 2430 effect, control); 2431 if (access_info.HasTransitionMap()) { 2432 // Allocate a HeapNumber for the new property. 2433 AllocationBuilder a(jsgraph(), effect, control); 2434 a.Allocate(HeapNumber::kSize, AllocationType::kYoung, 2435 Type::OtherInternal()); 2436 a.Store(AccessBuilder::ForMap(), 2437 MakeRef(broker(), factory()->heap_number_map())); 2438 FieldAccess value_field_access = AccessBuilder::ForHeapNumberValue(); 2439 value_field_access.const_field_info = field_access.const_field_info; 2440 a.Store(value_field_access, value); 2441 value = effect = a.Finish(); 2442 2443 field_access.type = Type::Any(); 2444 field_access.machine_type = MachineType::TaggedPointer(); 2445 field_access.write_barrier_kind = kPointerWriteBarrier; 2446 } else { 2447 // We just store directly to the HeapNumber. 2448 FieldAccess const storage_access = { 2449 kTaggedBase, 2450 field_index.offset(), 2451 name.object(), 2452 MaybeHandle<Map>(), 2453 Type::OtherInternal(), 2454 MachineType::TaggedPointer(), 2455 kPointerWriteBarrier, 2456 access_info.GetConstFieldInfo(), 2457 access_mode == AccessMode::kStoreInLiteral}; 2458 storage = effect = 2459 graph()->NewNode(simplified()->LoadField(storage_access), storage, 2460 effect, control); 2461 field_access.offset = HeapNumber::kValueOffset; 2462 field_access.name = MaybeHandle<Name>(); 2463 field_access.machine_type = MachineType::Float64(); 2464 } 2465 if (store_to_existing_constant_field) { 2466 DCHECK(!access_info.HasTransitionMap()); 2467 // If the field is constant check that the value we are going 2468 // to store matches current value. 2469 Node* current_value = effect = graph()->NewNode( 2470 simplified()->LoadField(field_access), storage, effect, control); 2471 2472 Node* check = 2473 graph()->NewNode(simplified()->SameValue(), current_value, value); 2474 effect = graph()->NewNode( 2475 simplified()->CheckIf(DeoptimizeReason::kWrongValue), check, 2476 effect, control); 2477 return ValueEffectControl(value, effect, control); 2478 } 2479 break; 2480 } 2481 case MachineRepresentation::kTaggedSigned: 2482 case MachineRepresentation::kTaggedPointer: 2483 case MachineRepresentation::kTagged: 2484 if (store_to_existing_constant_field) { 2485 DCHECK(!access_info.HasTransitionMap()); 2486 // If the field is constant check that the value we are going 2487 // to store matches current value. 2488 Node* current_value = effect = graph()->NewNode( 2489 simplified()->LoadField(field_access), storage, effect, control); 2490 2491 Node* check = graph()->NewNode(simplified()->SameValueNumbersOnly(), 2492 current_value, value); 2493 effect = graph()->NewNode( 2494 simplified()->CheckIf(DeoptimizeReason::kWrongValue), check, 2495 effect, control); 2496 return ValueEffectControl(value, effect, control); 2497 } 2498 2499 if (field_representation == MachineRepresentation::kTaggedSigned) { 2500 value = effect = graph()->NewNode( 2501 simplified()->CheckSmi(FeedbackSource()), value, effect, control); 2502 field_access.write_barrier_kind = kNoWriteBarrier; 2503 2504 } else if (field_representation == 2505 MachineRepresentation::kTaggedPointer) { 2506 base::Optional<MapRef> field_map = access_info.field_map(); 2507 if (field_map.has_value()) { 2508 // Emit a map check for the value. 2509 effect = 2510 graph()->NewNode(simplified()->CheckMaps( 2511 CheckMapsFlag::kNone, 2512 ZoneHandleSet<Map>(field_map->object())), 2513 value, effect, control); 2514 } else { 2515 // Ensure that {value} is a HeapObject. 2516 value = effect = graph()->NewNode(simplified()->CheckHeapObject(), 2517 value, effect, control); 2518 } 2519 field_access.write_barrier_kind = kPointerWriteBarrier; 2520 2521 } else { 2522 DCHECK(field_representation == MachineRepresentation::kTagged); 2523 } 2524 break; 2525 case MachineRepresentation::kNone: 2526 case MachineRepresentation::kBit: 2527 case MachineRepresentation::kCompressedPointer: 2528 case MachineRepresentation::kCompressed: 2529 case MachineRepresentation::kSandboxedPointer: 2530 case MachineRepresentation::kWord8: 2531 case MachineRepresentation::kWord16: 2532 case MachineRepresentation::kWord32: 2533 case MachineRepresentation::kWord64: 2534 case MachineRepresentation::kFloat32: 2535 case MachineRepresentation::kSimd128: 2536 case MachineRepresentation::kMapWord: 2537 UNREACHABLE(); 2538 } 2539 // Check if we need to perform a transitioning store. 2540 base::Optional<MapRef> transition_map = access_info.transition_map(); 2541 if (transition_map.has_value()) { 2542 // Check if we need to grow the properties backing store 2543 // with this transitioning store. 2544 MapRef transition_map_ref = transition_map.value(); 2545 MapRef original_map = transition_map_ref.GetBackPointer().AsMap(); 2546 if (original_map.UnusedPropertyFields() == 0) { 2547 DCHECK(!field_index.is_inobject()); 2548 2549 // Reallocate the properties {storage}. 2550 storage = effect = BuildExtendPropertiesBackingStore( 2551 original_map, storage, effect, control); 2552 2553 // Perform the actual store. 2554 effect = graph()->NewNode(simplified()->StoreField(field_access), 2555 storage, value, effect, control); 2556 2557 // Atomically switch to the new properties below. 2558 field_access = AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(); 2559 value = storage; 2560 storage = receiver; 2561 } 2562 effect = graph()->NewNode( 2563 common()->BeginRegion(RegionObservability::kObservable), effect); 2564 effect = graph()->NewNode( 2565 simplified()->StoreField(AccessBuilder::ForMap()), receiver, 2566 jsgraph()->Constant(transition_map_ref), effect, control); 2567 effect = graph()->NewNode(simplified()->StoreField(field_access), storage, 2568 value, effect, control); 2569 effect = graph()->NewNode(common()->FinishRegion(), 2570 jsgraph()->UndefinedConstant(), effect); 2571 } else { 2572 // Regular non-transitioning field store. 2573 effect = graph()->NewNode(simplified()->StoreField(field_access), storage, 2574 value, effect, control); 2575 } 2576 } 2577 2578 return ValueEffectControl(value, effect, control); 2579} 2580 2581Reduction 2582JSNativeContextSpecialization::ReduceJSDefineKeyedOwnPropertyInLiteral( 2583 Node* node) { 2584 JSDefineKeyedOwnPropertyInLiteralNode n(node); 2585 FeedbackParameter const& p = n.Parameters(); 2586 if (!p.feedback().IsValid()) return NoChange(); 2587 2588 NumberMatcher mflags(n.flags()); 2589 CHECK(mflags.HasResolvedValue()); 2590 DefineKeyedOwnPropertyInLiteralFlags cflags(mflags.ResolvedValue()); 2591 DCHECK(!(cflags & DefineKeyedOwnPropertyInLiteralFlag::kDontEnum)); 2592 if (cflags & DefineKeyedOwnPropertyInLiteralFlag::kSetFunctionName) 2593 return NoChange(); 2594 2595 return ReducePropertyAccess(node, n.name(), base::nullopt, n.value(), 2596 FeedbackSource(p.feedback()), 2597 AccessMode::kStoreInLiteral); 2598} 2599 2600Reduction JSNativeContextSpecialization::ReduceJSStoreInArrayLiteral( 2601 Node* node) { 2602 JSStoreInArrayLiteralNode n(node); 2603 FeedbackParameter const& p = n.Parameters(); 2604 if (!p.feedback().IsValid()) return NoChange(); 2605 return ReducePropertyAccess(node, n.index(), base::nullopt, n.value(), 2606 FeedbackSource(p.feedback()), 2607 AccessMode::kStoreInLiteral); 2608} 2609 2610Reduction JSNativeContextSpecialization::ReduceJSToObject(Node* node) { 2611 DCHECK_EQ(IrOpcode::kJSToObject, node->opcode()); 2612 Node* receiver = NodeProperties::GetValueInput(node, 0); 2613 Effect effect{NodeProperties::GetEffectInput(node)}; 2614 2615 MapInference inference(broker(), receiver, effect); 2616 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAreJSReceiver()) { 2617 return NoChange(); 2618 } 2619 2620 ReplaceWithValue(node, receiver, effect); 2621 return Replace(receiver); 2622} 2623 2624namespace { 2625 2626ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) { 2627 switch (kind) { 2628#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ 2629 case TYPE##_ELEMENTS: \ 2630 return kExternal##Type##Array; 2631 TYPED_ARRAYS(TYPED_ARRAY_CASE) 2632#undef TYPED_ARRAY_CASE 2633 default: 2634 break; 2635 } 2636 UNREACHABLE(); 2637} 2638 2639} // namespace 2640 2641JSNativeContextSpecialization::ValueEffectControl 2642JSNativeContextSpecialization::BuildElementAccess( 2643 Node* receiver, Node* index, Node* value, Node* effect, Node* control, 2644 ElementAccessInfo const& access_info, KeyedAccessMode const& keyed_mode) { 2645 // TODO(bmeurer): We currently specialize based on elements kind. We should 2646 // also be able to properly support strings and other JSObjects here. 2647 ElementsKind elements_kind = access_info.elements_kind(); 2648 ZoneVector<MapRef> const& receiver_maps = 2649 access_info.lookup_start_object_maps(); 2650 2651 if (IsTypedArrayElementsKind(elements_kind)) { 2652 Node* buffer_or_receiver = receiver; 2653 Node* length; 2654 Node* base_pointer; 2655 Node* external_pointer; 2656 2657 // Check if we can constant-fold information about the {receiver} (e.g. 2658 // for asm.js-like code patterns). 2659 base::Optional<JSTypedArrayRef> typed_array = 2660 GetTypedArrayConstant(broker(), receiver); 2661 if (typed_array.has_value()) { 2662 length = jsgraph()->Constant(static_cast<double>(typed_array->length())); 2663 2664 DCHECK(!typed_array->is_on_heap()); 2665 // Load the (known) data pointer for the {receiver} and set {base_pointer} 2666 // and {external_pointer} to the values that will allow to generate typed 2667 // element accesses using the known data pointer. 2668 // The data pointer might be invalid if the {buffer} was detached, 2669 // so we need to make sure that any access is properly guarded. 2670 base_pointer = jsgraph()->ZeroConstant(); 2671 external_pointer = jsgraph()->PointerConstant(typed_array->data_ptr()); 2672 } else { 2673 // Load the {receiver}s length. 2674 length = effect = graph()->NewNode( 2675 simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()), 2676 receiver, effect, control); 2677 2678 // Load the base pointer for the {receiver}. This will always be Smi 2679 // zero unless we allow on-heap TypedArrays, which is only the case 2680 // for Chrome. Node and Electron both set this limit to 0. Setting 2681 // the base to Smi zero here allows the EffectControlLinearizer to 2682 // optimize away the tricky part of the access later. 2683 if (JSTypedArray::kMaxSizeInHeap == 0) { 2684 base_pointer = jsgraph()->ZeroConstant(); 2685 } else { 2686 base_pointer = effect = 2687 graph()->NewNode(simplified()->LoadField( 2688 AccessBuilder::ForJSTypedArrayBasePointer()), 2689 receiver, effect, control); 2690 } 2691 2692 // Load the external pointer for the {receiver}. 2693 external_pointer = effect = 2694 graph()->NewNode(simplified()->LoadField( 2695 AccessBuilder::ForJSTypedArrayExternalPointer()), 2696 receiver, effect, control); 2697 } 2698 2699 // See if we can skip the detaching check. 2700 if (!dependencies()->DependOnArrayBufferDetachingProtector()) { 2701 // Load the buffer for the {receiver}. 2702 Node* buffer = 2703 typed_array.has_value() 2704 ? jsgraph()->Constant(typed_array->buffer()) 2705 : (effect = graph()->NewNode( 2706 simplified()->LoadField( 2707 AccessBuilder::ForJSArrayBufferViewBuffer()), 2708 receiver, effect, control)); 2709 2710 // Deopt if the {buffer} was detached. 2711 // Note: A detached buffer leads to megamorphic feedback. 2712 Node* buffer_bit_field = effect = graph()->NewNode( 2713 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), 2714 buffer, effect, control); 2715 Node* check = graph()->NewNode( 2716 simplified()->NumberEqual(), 2717 graph()->NewNode( 2718 simplified()->NumberBitwiseAnd(), buffer_bit_field, 2719 jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)), 2720 jsgraph()->ZeroConstant()); 2721 effect = graph()->NewNode( 2722 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached), 2723 check, effect, control); 2724 2725 // Retain the {buffer} instead of {receiver} to reduce live ranges. 2726 buffer_or_receiver = buffer; 2727 } 2728 2729 enum Situation { kBoundsCheckDone, kHandleOOB_SmiCheckDone }; 2730 Situation situation; 2731 if ((keyed_mode.IsLoad() && 2732 keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS) || 2733 (keyed_mode.IsStore() && 2734 keyed_mode.store_mode() == STORE_IGNORE_OUT_OF_BOUNDS)) { 2735 // Only check that the {index} is in SignedSmall range. We do the actual 2736 // bounds check below and just skip the property access if it's out of 2737 // bounds for the {receiver}. 2738 index = effect = graph()->NewNode( 2739 simplified()->CheckSmi(FeedbackSource()), index, effect, control); 2740 2741 // Cast the {index} to Unsigned32 range, so that the bounds checks 2742 // below are performed on unsigned values, which means that all the 2743 // Negative32 values are treated as out-of-bounds. 2744 index = graph()->NewNode(simplified()->NumberToUint32(), index); 2745 situation = kHandleOOB_SmiCheckDone; 2746 } else { 2747 // Check that the {index} is in the valid range for the {receiver}. 2748 index = effect = graph()->NewNode( 2749 simplified()->CheckBounds( 2750 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero), 2751 index, length, effect, control); 2752 situation = kBoundsCheckDone; 2753 } 2754 2755 // Access the actual element. 2756 ExternalArrayType external_array_type = 2757 GetArrayTypeFromElementsKind(elements_kind); 2758 switch (keyed_mode.access_mode()) { 2759 case AccessMode::kLoad: { 2760 // Check if we can return undefined for out-of-bounds loads. 2761 if (situation == kHandleOOB_SmiCheckDone) { 2762 Node* check = 2763 graph()->NewNode(simplified()->NumberLessThan(), index, length); 2764 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 2765 check, control); 2766 2767 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 2768 Node* etrue = effect; 2769 Node* vtrue; 2770 { 2771 // Do a real bounds check against {length}. This is in order to 2772 // protect against a potential typer bug leading to the elimination 2773 // of the NumberLessThan above. 2774 index = etrue = graph()->NewNode( 2775 simplified()->CheckBounds( 2776 FeedbackSource(), 2777 CheckBoundsFlag::kConvertStringAndMinusZero | 2778 CheckBoundsFlag::kAbortOnOutOfBounds), 2779 index, length, etrue, if_true); 2780 2781 // Perform the actual load 2782 vtrue = etrue = graph()->NewNode( 2783 simplified()->LoadTypedElement(external_array_type), 2784 buffer_or_receiver, base_pointer, external_pointer, index, 2785 etrue, if_true); 2786 } 2787 2788 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 2789 Node* efalse = effect; 2790 Node* vfalse; 2791 { 2792 // Materialize undefined for out-of-bounds loads. 2793 vfalse = jsgraph()->UndefinedConstant(); 2794 } 2795 2796 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 2797 effect = 2798 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 2799 value = 2800 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 2801 vtrue, vfalse, control); 2802 } else { 2803 // Perform the actual load. 2804 DCHECK_EQ(kBoundsCheckDone, situation); 2805 value = effect = graph()->NewNode( 2806 simplified()->LoadTypedElement(external_array_type), 2807 buffer_or_receiver, base_pointer, external_pointer, index, effect, 2808 control); 2809 } 2810 break; 2811 } 2812 case AccessMode::kStoreInLiteral: 2813 case AccessMode::kDefine: 2814 UNREACHABLE(); 2815 case AccessMode::kStore: { 2816 // Ensure that the {value} is actually a Number or an Oddball, 2817 // and truncate it to a Number appropriately. 2818 value = effect = graph()->NewNode( 2819 simplified()->SpeculativeToNumber( 2820 NumberOperationHint::kNumberOrOddball, FeedbackSource()), 2821 value, effect, control); 2822 2823 // Introduce the appropriate truncation for {value}. Currently we 2824 // only need to do this for ClamedUint8Array {receiver}s, as the 2825 // other truncations are implicit in the StoreTypedElement, but we 2826 // might want to change that at some point. 2827 if (external_array_type == kExternalUint8ClampedArray) { 2828 value = graph()->NewNode(simplified()->NumberToUint8Clamped(), value); 2829 } 2830 2831 if (situation == kHandleOOB_SmiCheckDone) { 2832 // We have to detect OOB stores and handle them without deopt (by 2833 // simply not performing them). 2834 Node* check = 2835 graph()->NewNode(simplified()->NumberLessThan(), index, length); 2836 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 2837 check, control); 2838 2839 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 2840 Node* etrue = effect; 2841 { 2842 // Do a real bounds check against {length}. This is in order to 2843 // protect against a potential typer bug leading to the elimination 2844 // of the NumberLessThan above. 2845 index = etrue = graph()->NewNode( 2846 simplified()->CheckBounds( 2847 FeedbackSource(), 2848 CheckBoundsFlag::kConvertStringAndMinusZero | 2849 CheckBoundsFlag::kAbortOnOutOfBounds), 2850 index, length, etrue, if_true); 2851 2852 // Perform the actual store. 2853 etrue = graph()->NewNode( 2854 simplified()->StoreTypedElement(external_array_type), 2855 buffer_or_receiver, base_pointer, external_pointer, index, 2856 value, etrue, if_true); 2857 } 2858 2859 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 2860 Node* efalse = effect; 2861 { 2862 // Just ignore the out-of-bounds write. 2863 } 2864 2865 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 2866 effect = 2867 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 2868 } else { 2869 // Perform the actual store 2870 DCHECK_EQ(kBoundsCheckDone, situation); 2871 effect = graph()->NewNode( 2872 simplified()->StoreTypedElement(external_array_type), 2873 buffer_or_receiver, base_pointer, external_pointer, index, value, 2874 effect, control); 2875 } 2876 break; 2877 } 2878 case AccessMode::kHas: 2879 if (situation == kHandleOOB_SmiCheckDone) { 2880 value = effect = 2881 graph()->NewNode(simplified()->SpeculativeNumberLessThan( 2882 NumberOperationHint::kSignedSmall), 2883 index, length, effect, control); 2884 } else { 2885 DCHECK_EQ(kBoundsCheckDone, situation); 2886 // For has-property on a typed array, all we need is a bounds check. 2887 value = jsgraph()->TrueConstant(); 2888 } 2889 break; 2890 } 2891 } else { 2892 // Load the elements for the {receiver}. 2893 Node* elements = effect = graph()->NewNode( 2894 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, 2895 effect, control); 2896 2897 // Don't try to store to a copy-on-write backing store (unless supported by 2898 // the store mode). 2899 if (IsAnyStore(keyed_mode.access_mode()) && 2900 IsSmiOrObjectElementsKind(elements_kind) && 2901 !IsCOWHandlingStoreMode(keyed_mode.store_mode())) { 2902 effect = graph()->NewNode( 2903 simplified()->CheckMaps( 2904 CheckMapsFlag::kNone, 2905 ZoneHandleSet<Map>(factory()->fixed_array_map())), 2906 elements, effect, control); 2907 } 2908 2909 // Check if the {receiver} is a JSArray. 2910 bool receiver_is_jsarray = HasOnlyJSArrayMaps(broker(), receiver_maps); 2911 2912 // Load the length of the {receiver}. 2913 Node* length = effect = 2914 receiver_is_jsarray 2915 ? graph()->NewNode( 2916 simplified()->LoadField( 2917 AccessBuilder::ForJSArrayLength(elements_kind)), 2918 receiver, effect, control) 2919 : graph()->NewNode( 2920 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), 2921 elements, effect, control); 2922 2923 // Check if we might need to grow the {elements} backing store. 2924 if (keyed_mode.IsStore() && IsGrowStoreMode(keyed_mode.store_mode())) { 2925 // For growing stores we validate the {index} below. 2926 } else if (keyed_mode.IsLoad() && 2927 keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS && 2928 CanTreatHoleAsUndefined(receiver_maps)) { 2929 // Check that the {index} is a valid array index, we do the actual 2930 // bounds check below and just skip the store below if it's out of 2931 // bounds for the {receiver}. 2932 index = effect = graph()->NewNode( 2933 simplified()->CheckBounds( 2934 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero), 2935 index, jsgraph()->Constant(Smi::kMaxValue), effect, control); 2936 } else { 2937 // Check that the {index} is in the valid range for the {receiver}. 2938 index = effect = graph()->NewNode( 2939 simplified()->CheckBounds( 2940 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero), 2941 index, length, effect, control); 2942 } 2943 2944 // Compute the element access. 2945 Type element_type = Type::NonInternal(); 2946 MachineType element_machine_type = MachineType::AnyTagged(); 2947 if (IsDoubleElementsKind(elements_kind)) { 2948 element_type = Type::Number(); 2949 element_machine_type = MachineType::Float64(); 2950 } else if (IsSmiElementsKind(elements_kind)) { 2951 element_type = Type::SignedSmall(); 2952 element_machine_type = MachineType::TaggedSigned(); 2953 } 2954 ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize, 2955 element_type, element_machine_type, 2956 kFullWriteBarrier}; 2957 2958 // Access the actual element. 2959 if (keyed_mode.access_mode() == AccessMode::kLoad) { 2960 // Compute the real element access type, which includes the hole in case 2961 // of holey backing stores. 2962 if (IsHoleyElementsKind(elements_kind)) { 2963 element_access.type = 2964 Type::Union(element_type, Type::Hole(), graph()->zone()); 2965 } 2966 if (elements_kind == HOLEY_ELEMENTS || 2967 elements_kind == HOLEY_SMI_ELEMENTS) { 2968 element_access.machine_type = MachineType::AnyTagged(); 2969 } 2970 2971 // Check if we can return undefined for out-of-bounds loads. 2972 if (keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS && 2973 CanTreatHoleAsUndefined(receiver_maps)) { 2974 Node* check = 2975 graph()->NewNode(simplified()->NumberLessThan(), index, length); 2976 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 2977 check, control); 2978 2979 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 2980 Node* etrue = effect; 2981 Node* vtrue; 2982 { 2983 // Do a real bounds check against {length}. This is in order to 2984 // protect against a potential typer bug leading to the elimination of 2985 // the NumberLessThan above. 2986 index = etrue = 2987 graph()->NewNode(simplified()->CheckBounds( 2988 FeedbackSource(), 2989 CheckBoundsFlag::kConvertStringAndMinusZero | 2990 CheckBoundsFlag::kAbortOnOutOfBounds), 2991 index, length, etrue, if_true); 2992 2993 // Perform the actual load 2994 vtrue = etrue = 2995 graph()->NewNode(simplified()->LoadElement(element_access), 2996 elements, index, etrue, if_true); 2997 2998 // Handle loading from holey backing stores correctly, by either 2999 // mapping the hole to undefined if possible, or deoptimizing 3000 // otherwise. 3001 if (elements_kind == HOLEY_ELEMENTS || 3002 elements_kind == HOLEY_SMI_ELEMENTS) { 3003 // Turn the hole into undefined. 3004 vtrue = graph()->NewNode( 3005 simplified()->ConvertTaggedHoleToUndefined(), vtrue); 3006 } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) { 3007 // Return the signaling NaN hole directly if all uses are 3008 // truncating. 3009 vtrue = etrue = graph()->NewNode( 3010 simplified()->CheckFloat64Hole( 3011 CheckFloat64HoleMode::kAllowReturnHole, FeedbackSource()), 3012 vtrue, etrue, if_true); 3013 } 3014 } 3015 3016 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 3017 Node* efalse = effect; 3018 Node* vfalse; 3019 { 3020 // Materialize undefined for out-of-bounds loads. 3021 vfalse = jsgraph()->UndefinedConstant(); 3022 } 3023 3024 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 3025 effect = 3026 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 3027 value = 3028 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 3029 vtrue, vfalse, control); 3030 } else { 3031 // Perform the actual load. 3032 value = effect = 3033 graph()->NewNode(simplified()->LoadElement(element_access), 3034 elements, index, effect, control); 3035 3036 // Handle loading from holey backing stores correctly, by either mapping 3037 // the hole to undefined if possible, or deoptimizing otherwise. 3038 if (elements_kind == HOLEY_ELEMENTS || 3039 elements_kind == HOLEY_SMI_ELEMENTS) { 3040 // Check if we are allowed to turn the hole into undefined. 3041 if (CanTreatHoleAsUndefined(receiver_maps)) { 3042 // Turn the hole into undefined. 3043 value = graph()->NewNode( 3044 simplified()->ConvertTaggedHoleToUndefined(), value); 3045 } else { 3046 // Bailout if we see the hole. 3047 value = effect = graph()->NewNode( 3048 simplified()->CheckNotTaggedHole(), value, effect, control); 3049 } 3050 } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) { 3051 // Perform the hole check on the result. 3052 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole; 3053 // Check if we are allowed to return the hole directly. 3054 if (CanTreatHoleAsUndefined(receiver_maps)) { 3055 // Return the signaling NaN hole directly if all uses are 3056 // truncating. 3057 mode = CheckFloat64HoleMode::kAllowReturnHole; 3058 } 3059 value = effect = graph()->NewNode( 3060 simplified()->CheckFloat64Hole(mode, FeedbackSource()), value, 3061 effect, control); 3062 } 3063 } 3064 } else if (keyed_mode.access_mode() == AccessMode::kHas) { 3065 // For packed arrays with NoElementsProctector valid, a bound check 3066 // is equivalent to HasProperty. 3067 value = effect = graph()->NewNode(simplified()->SpeculativeNumberLessThan( 3068 NumberOperationHint::kSignedSmall), 3069 index, length, effect, control); 3070 if (IsHoleyElementsKind(elements_kind)) { 3071 // If the index is in bounds, do a load and hole check. 3072 3073 Node* branch = graph()->NewNode(common()->Branch(), value, control); 3074 3075 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 3076 Node* efalse = effect; 3077 Node* vfalse = jsgraph()->FalseConstant(); 3078 3079 element_access.type = 3080 Type::Union(element_type, Type::Hole(), graph()->zone()); 3081 3082 if (elements_kind == HOLEY_ELEMENTS || 3083 elements_kind == HOLEY_SMI_ELEMENTS) { 3084 element_access.machine_type = MachineType::AnyTagged(); 3085 } 3086 3087 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 3088 Node* etrue = effect; 3089 3090 Node* checked = etrue = graph()->NewNode( 3091 simplified()->CheckBounds( 3092 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero), 3093 index, length, etrue, if_true); 3094 3095 Node* element = etrue = 3096 graph()->NewNode(simplified()->LoadElement(element_access), 3097 elements, checked, etrue, if_true); 3098 3099 Node* vtrue; 3100 if (CanTreatHoleAsUndefined(receiver_maps)) { 3101 if (elements_kind == HOLEY_ELEMENTS || 3102 elements_kind == HOLEY_SMI_ELEMENTS) { 3103 // Check if we are allowed to turn the hole into undefined. 3104 // Turn the hole into undefined. 3105 vtrue = graph()->NewNode(simplified()->ReferenceEqual(), element, 3106 jsgraph()->TheHoleConstant()); 3107 } else { 3108 vtrue = 3109 graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); 3110 } 3111 3112 // has == !IsHole 3113 vtrue = graph()->NewNode(simplified()->BooleanNot(), vtrue); 3114 } else { 3115 if (elements_kind == HOLEY_ELEMENTS || 3116 elements_kind == HOLEY_SMI_ELEMENTS) { 3117 // Bailout if we see the hole. 3118 etrue = graph()->NewNode(simplified()->CheckNotTaggedHole(), 3119 element, etrue, if_true); 3120 } else { 3121 etrue = graph()->NewNode( 3122 simplified()->CheckFloat64Hole( 3123 CheckFloat64HoleMode::kNeverReturnHole, FeedbackSource()), 3124 element, etrue, if_true); 3125 } 3126 3127 vtrue = jsgraph()->TrueConstant(); 3128 } 3129 3130 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 3131 effect = 3132 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 3133 value = 3134 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 3135 vtrue, vfalse, control); 3136 } 3137 } else { 3138 DCHECK(keyed_mode.access_mode() == AccessMode::kStore || 3139 keyed_mode.access_mode() == AccessMode::kStoreInLiteral || 3140 keyed_mode.access_mode() == AccessMode::kDefine); 3141 3142 if (IsSmiElementsKind(elements_kind)) { 3143 value = effect = graph()->NewNode( 3144 simplified()->CheckSmi(FeedbackSource()), value, effect, control); 3145 } else if (IsDoubleElementsKind(elements_kind)) { 3146 value = effect = 3147 graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), value, 3148 effect, control); 3149 // Make sure we do not store signalling NaNs into double arrays. 3150 value = graph()->NewNode(simplified()->NumberSilenceNaN(), value); 3151 } 3152 3153 // Ensure that copy-on-write backing store is writable. 3154 if (IsSmiOrObjectElementsKind(elements_kind) && 3155 keyed_mode.store_mode() == STORE_HANDLE_COW) { 3156 elements = effect = 3157 graph()->NewNode(simplified()->EnsureWritableFastElements(), 3158 receiver, elements, effect, control); 3159 } else if (IsGrowStoreMode(keyed_mode.store_mode())) { 3160 // Determine the length of the {elements} backing store. 3161 Node* elements_length = effect = graph()->NewNode( 3162 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), 3163 elements, effect, control); 3164 3165 // Validate the {index} depending on holeyness: 3166 // 3167 // For HOLEY_*_ELEMENTS the {index} must not exceed the {elements} 3168 // backing store capacity plus the maximum allowed gap, as otherwise 3169 // the (potential) backing store growth would normalize and thus 3170 // the elements kind of the {receiver} would change to slow mode. 3171 // 3172 // For PACKED_*_ELEMENTS the {index} must be within the range 3173 // [0,length+1[ to be valid. In case {index} equals {length}, 3174 // the {receiver} will be extended, but kept packed. 3175 Node* limit = 3176 IsHoleyElementsKind(elements_kind) 3177 ? graph()->NewNode(simplified()->NumberAdd(), elements_length, 3178 jsgraph()->Constant(JSObject::kMaxGap)) 3179 : graph()->NewNode(simplified()->NumberAdd(), length, 3180 jsgraph()->OneConstant()); 3181 index = effect = graph()->NewNode( 3182 simplified()->CheckBounds( 3183 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero), 3184 index, limit, effect, control); 3185 3186 // Grow {elements} backing store if necessary. 3187 GrowFastElementsMode mode = 3188 IsDoubleElementsKind(elements_kind) 3189 ? GrowFastElementsMode::kDoubleElements 3190 : GrowFastElementsMode::kSmiOrObjectElements; 3191 elements = effect = graph()->NewNode( 3192 simplified()->MaybeGrowFastElements(mode, FeedbackSource()), 3193 receiver, elements, index, elements_length, effect, control); 3194 3195 // If we didn't grow {elements}, it might still be COW, in which case we 3196 // copy it now. 3197 if (IsSmiOrObjectElementsKind(elements_kind) && 3198 keyed_mode.store_mode() == STORE_AND_GROW_HANDLE_COW) { 3199 elements = effect = 3200 graph()->NewNode(simplified()->EnsureWritableFastElements(), 3201 receiver, elements, effect, control); 3202 } 3203 3204 // Also update the "length" property if {receiver} is a JSArray. 3205 if (receiver_is_jsarray) { 3206 Node* check = 3207 graph()->NewNode(simplified()->NumberLessThan(), index, length); 3208 Node* branch = graph()->NewNode(common()->Branch(), check, control); 3209 3210 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 3211 Node* etrue = effect; 3212 { 3213 // We don't need to do anything, the {index} is within 3214 // the valid bounds for the JSArray {receiver}. 3215 } 3216 3217 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 3218 Node* efalse = effect; 3219 { 3220 // Update the JSArray::length field. Since this is observable, 3221 // there must be no other check after this. 3222 Node* new_length = graph()->NewNode( 3223 simplified()->NumberAdd(), index, jsgraph()->OneConstant()); 3224 efalse = graph()->NewNode( 3225 simplified()->StoreField( 3226 AccessBuilder::ForJSArrayLength(elements_kind)), 3227 receiver, new_length, efalse, if_false); 3228 } 3229 3230 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 3231 effect = 3232 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 3233 } 3234 } 3235 3236 // Perform the actual element access. 3237 effect = graph()->NewNode(simplified()->StoreElement(element_access), 3238 elements, index, value, effect, control); 3239 } 3240 } 3241 3242 return ValueEffectControl(value, effect, control); 3243} 3244 3245Node* JSNativeContextSpecialization::BuildIndexedStringLoad( 3246 Node* receiver, Node* index, Node* length, Node** effect, Node** control, 3247 KeyedAccessLoadMode load_mode) { 3248 if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS && 3249 dependencies()->DependOnNoElementsProtector()) { 3250 // Ensure that the {index} is a valid String length. 3251 index = *effect = graph()->NewNode( 3252 simplified()->CheckBounds(FeedbackSource(), 3253 CheckBoundsFlag::kConvertStringAndMinusZero), 3254 index, jsgraph()->Constant(String::kMaxLength), *effect, *control); 3255 3256 // Load the single character string from {receiver} or yield 3257 // undefined if the {index} is not within the valid bounds. 3258 Node* check = 3259 graph()->NewNode(simplified()->NumberLessThan(), index, length); 3260 Node* branch = 3261 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, *control); 3262 3263 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 3264 // Do a real bounds check against {length}. This is in order to protect 3265 // against a potential typer bug leading to the elimination of the 3266 // NumberLessThan above. 3267 Node* etrue = index = graph()->NewNode( 3268 simplified()->CheckBounds(FeedbackSource(), 3269 CheckBoundsFlag::kConvertStringAndMinusZero | 3270 CheckBoundsFlag::kAbortOnOutOfBounds), 3271 index, length, *effect, if_true); 3272 Node* vtrue = etrue = graph()->NewNode(simplified()->StringCharCodeAt(), 3273 receiver, index, etrue, if_true); 3274 vtrue = graph()->NewNode(simplified()->StringFromSingleCharCode(), vtrue); 3275 3276 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 3277 Node* vfalse = jsgraph()->UndefinedConstant(); 3278 3279 *control = graph()->NewNode(common()->Merge(2), if_true, if_false); 3280 *effect = 3281 graph()->NewNode(common()->EffectPhi(2), etrue, *effect, *control); 3282 return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 3283 vtrue, vfalse, *control); 3284 } else { 3285 // Ensure that {index} is less than {receiver} length. 3286 index = *effect = graph()->NewNode( 3287 simplified()->CheckBounds(FeedbackSource(), 3288 CheckBoundsFlag::kConvertStringAndMinusZero), 3289 index, length, *effect, *control); 3290 3291 // Return the character from the {receiver} as single character string. 3292 Node* value = *effect = graph()->NewNode( 3293 simplified()->StringCharCodeAt(), receiver, index, *effect, *control); 3294 value = graph()->NewNode(simplified()->StringFromSingleCharCode(), value); 3295 return value; 3296 } 3297} 3298 3299Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore( 3300 const MapRef& map, Node* properties, Node* effect, Node* control) { 3301 // TODO(bmeurer/jkummerow): Property deletions can undo map transitions 3302 // while keeping the backing store around, meaning that even though the 3303 // map might believe that objects have no unused property fields, there 3304 // might actually be some. It would be nice to not create a new backing 3305 // store in that case (i.e. when properties->length() >= new_length). 3306 // However, introducing branches and Phi nodes here would make it more 3307 // difficult for escape analysis to get rid of the backing stores used 3308 // for intermediate states of chains of property additions. That makes 3309 // it unclear what the best approach is here. 3310 DCHECK_EQ(0, map.UnusedPropertyFields()); 3311 // Compute the length of the old {properties} and the new properties. 3312 int length = map.NextFreePropertyIndex() - map.GetInObjectProperties(); 3313 int new_length = length + JSObject::kFieldsAdded; 3314 // Collect the field values from the {properties}. 3315 ZoneVector<Node*> values(zone()); 3316 values.reserve(new_length); 3317 for (int i = 0; i < length; ++i) { 3318 Node* value = effect = graph()->NewNode( 3319 simplified()->LoadField(AccessBuilder::ForFixedArraySlot(i)), 3320 properties, effect, control); 3321 values.push_back(value); 3322 } 3323 // Initialize the new fields to undefined. 3324 for (int i = 0; i < JSObject::kFieldsAdded; ++i) { 3325 values.push_back(jsgraph()->UndefinedConstant()); 3326 } 3327 3328 // Compute new length and hash. 3329 Node* hash; 3330 if (length == 0) { 3331 hash = graph()->NewNode( 3332 common()->Select(MachineRepresentation::kTaggedSigned), 3333 graph()->NewNode(simplified()->ObjectIsSmi(), properties), properties, 3334 jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel)); 3335 hash = effect = graph()->NewNode(common()->TypeGuard(Type::SignedSmall()), 3336 hash, effect, control); 3337 hash = 3338 graph()->NewNode(simplified()->NumberShiftLeft(), hash, 3339 jsgraph()->Constant(PropertyArray::HashField::kShift)); 3340 } else { 3341 hash = effect = graph()->NewNode( 3342 simplified()->LoadField(AccessBuilder::ForPropertyArrayLengthAndHash()), 3343 properties, effect, control); 3344 hash = 3345 graph()->NewNode(simplified()->NumberBitwiseAnd(), hash, 3346 jsgraph()->Constant(PropertyArray::HashField::kMask)); 3347 } 3348 Node* new_length_and_hash = graph()->NewNode( 3349 simplified()->NumberBitwiseOr(), jsgraph()->Constant(new_length), hash); 3350 // TDOO(jarin): Fix the typer to infer tighter bound for NumberBitwiseOr. 3351 new_length_and_hash = effect = 3352 graph()->NewNode(common()->TypeGuard(Type::SignedSmall()), 3353 new_length_and_hash, effect, control); 3354 3355 // Allocate and initialize the new properties. 3356 AllocationBuilder a(jsgraph(), effect, control); 3357 a.Allocate(PropertyArray::SizeFor(new_length), AllocationType::kYoung, 3358 Type::OtherInternal()); 3359 a.Store(AccessBuilder::ForMap(), jsgraph()->PropertyArrayMapConstant()); 3360 a.Store(AccessBuilder::ForPropertyArrayLengthAndHash(), new_length_and_hash); 3361 for (int i = 0; i < new_length; ++i) { 3362 a.Store(AccessBuilder::ForFixedArraySlot(i), values[i]); 3363 } 3364 return a.Finish(); 3365} 3366 3367Node* JSNativeContextSpecialization::BuildCheckEqualsName(NameRef const& name, 3368 Node* value, 3369 Node* effect, 3370 Node* control) { 3371 DCHECK(name.IsUniqueName()); 3372 Operator const* const op = 3373 name.IsSymbol() ? simplified()->CheckEqualsSymbol() 3374 : simplified()->CheckEqualsInternalizedString(); 3375 return graph()->NewNode(op, jsgraph()->Constant(name), value, effect, 3376 control); 3377} 3378 3379bool JSNativeContextSpecialization::CanTreatHoleAsUndefined( 3380 ZoneVector<MapRef> const& receiver_maps) { 3381 // Check if all {receiver_maps} have one of the initial Array.prototype 3382 // or Object.prototype objects as their prototype (in any of the current 3383 // native contexts, as the global Array protector works isolate-wide). 3384 for (MapRef receiver_map : receiver_maps) { 3385 ObjectRef receiver_prototype = receiver_map.prototype(); 3386 if (!receiver_prototype.IsJSObject() || 3387 !broker()->IsArrayOrObjectPrototype(receiver_prototype.AsJSObject())) { 3388 return false; 3389 } 3390 } 3391 3392 // Check if the array prototype chain is intact. 3393 return dependencies()->DependOnNoElementsProtector(); 3394} 3395 3396bool JSNativeContextSpecialization::InferMaps(Node* object, Effect effect, 3397 ZoneVector<MapRef>* maps) const { 3398 ZoneRefUnorderedSet<MapRef> map_set(broker()->zone()); 3399 NodeProperties::InferMapsResult result = 3400 NodeProperties::InferMapsUnsafe(broker(), object, effect, &map_set); 3401 if (result == NodeProperties::kReliableMaps) { 3402 for (const MapRef& map : map_set) { 3403 maps->push_back(map); 3404 } 3405 return true; 3406 } else if (result == NodeProperties::kUnreliableMaps) { 3407 // For untrusted maps, we can still use the information 3408 // if the maps are stable. 3409 for (const MapRef& map : map_set) { 3410 if (!map.is_stable()) return false; 3411 } 3412 for (const MapRef& map : map_set) { 3413 maps->push_back(map); 3414 } 3415 return true; 3416 } 3417 return false; 3418} 3419 3420base::Optional<MapRef> JSNativeContextSpecialization::InferRootMap( 3421 Node* object) const { 3422 HeapObjectMatcher m(object); 3423 if (m.HasResolvedValue()) { 3424 MapRef map = m.Ref(broker()).map(); 3425 return map.FindRootMap(); 3426 } else if (m.IsJSCreate()) { 3427 base::Optional<MapRef> initial_map = 3428 NodeProperties::GetJSCreateMap(broker(), object); 3429 if (initial_map.has_value()) { 3430 DCHECK(initial_map->equals(initial_map->FindRootMap())); 3431 return *initial_map; 3432 } 3433 } 3434 return base::nullopt; 3435} 3436 3437Node* JSNativeContextSpecialization::BuildLoadPrototypeFromObject( 3438 Node* object, Node* effect, Node* control) { 3439 Node* map = effect = 3440 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), object, 3441 effect, control); 3442 return graph()->NewNode( 3443 simplified()->LoadField(AccessBuilder::ForMapPrototype()), map, effect, 3444 control); 3445} 3446 3447Graph* JSNativeContextSpecialization::graph() const { 3448 return jsgraph()->graph(); 3449} 3450 3451Isolate* JSNativeContextSpecialization::isolate() const { 3452 return jsgraph()->isolate(); 3453} 3454 3455Factory* JSNativeContextSpecialization::factory() const { 3456 return isolate()->factory(); 3457} 3458 3459CommonOperatorBuilder* JSNativeContextSpecialization::common() const { 3460 return jsgraph()->common(); 3461} 3462 3463JSOperatorBuilder* JSNativeContextSpecialization::javascript() const { 3464 return jsgraph()->javascript(); 3465} 3466 3467SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { 3468 return jsgraph()->simplified(); 3469} 3470 3471} // namespace compiler 3472} // namespace internal 3473} // namespace v8 3474