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-call-reducer.h" 6 7#include <functional> 8 9#include "src/api/api-inl.h" 10#include "src/base/small-vector.h" 11#include "src/builtins/builtins-promise.h" 12#include "src/builtins/builtins-utils.h" 13#include "src/codegen/code-factory.h" 14#include "src/codegen/tnode.h" 15#include "src/compiler/access-builder.h" 16#include "src/compiler/access-info.h" 17#include "src/compiler/allocation-builder-inl.h" 18#include "src/compiler/allocation-builder.h" 19#include "src/compiler/common-operator.h" 20#include "src/compiler/compilation-dependencies.h" 21#include "src/compiler/fast-api-calls.h" 22#include "src/compiler/feedback-source.h" 23#include "src/compiler/graph-assembler.h" 24#include "src/compiler/js-graph.h" 25#include "src/compiler/linkage.h" 26#include "src/compiler/map-inference.h" 27#include "src/compiler/node-matchers.h" 28#include "src/compiler/opcodes.h" 29#include "src/compiler/property-access-builder.h" 30#include "src/compiler/simplified-operator.h" 31#include "src/compiler/state-values-utils.h" 32#include "src/compiler/type-cache.h" 33#include "src/ic/call-optimization.h" 34#include "src/logging/counters.h" 35#include "src/objects/arguments-inl.h" 36#include "src/objects/feedback-vector-inl.h" 37#include "src/objects/js-array-buffer-inl.h" 38#include "src/objects/js-array-inl.h" 39#include "src/objects/js-function.h" 40#include "src/objects/objects-inl.h" 41#include "src/objects/ordered-hash-table.h" 42#include "src/objects/string-inl.h" 43 44#ifdef V8_INTL_SUPPORT 45#include "src/objects/intl-objects.h" 46#endif 47 48namespace v8 { 49namespace internal { 50namespace compiler { 51 52// Shorter lambda declarations with less visual clutter. 53#define _ [&]() 54 55class JSCallReducerAssembler : public JSGraphAssembler { 56 protected: 57 class CatchScope; 58 59 private: 60 static constexpr bool kMarkLoopExits = true; 61 62 public: 63 JSCallReducerAssembler(JSCallReducer* reducer, Node* node) 64 : JSGraphAssembler( 65 reducer->JSGraphForGraphAssembler(), 66 reducer->ZoneForGraphAssembler(), 67 [reducer](Node* n) { reducer->RevisitForGraphAssembler(n); }, 68 kMarkLoopExits), 69 dependencies_(reducer->dependencies()), 70 node_(node), 71 outermost_catch_scope_( 72 CatchScope::Outermost(reducer->ZoneForGraphAssembler())), 73 catch_scope_(&outermost_catch_scope_) { 74 InitializeEffectControl(NodeProperties::GetEffectInput(node), 75 NodeProperties::GetControlInput(node)); 76 77 // Finish initializing the outermost catch scope. 78 bool has_handler = 79 NodeProperties::IsExceptionalCall(node, &outermost_handler_); 80 outermost_catch_scope_.set_has_handler(has_handler); 81 outermost_catch_scope_.set_gasm(this); 82 } 83 84 TNode<Object> ReduceJSCallWithArrayLikeOrSpreadOfEmpty( 85 std::unordered_set<Node*>* generated_calls_with_array_like_or_spread); 86 TNode<Object> ReduceMathUnary(const Operator* op); 87 TNode<Object> ReduceMathBinary(const Operator* op); 88 TNode<String> ReduceStringPrototypeSubstring(); 89 TNode<Boolean> ReduceStringPrototypeStartsWith(); 90 TNode<Boolean> ReduceStringPrototypeStartsWith( 91 const StringRef& search_element_string); 92 TNode<String> ReduceStringPrototypeSlice(); 93 94 TNode<Object> TargetInput() const { return JSCallNode{node_ptr()}.target(); } 95 96 template <typename T> 97 TNode<T> ReceiverInputAs() const { 98 return TNode<T>::UncheckedCast(JSCallNode{node_ptr()}.receiver()); 99 } 100 101 TNode<Object> ReceiverInput() const { return ReceiverInputAs<Object>(); } 102 103 CatchScope* catch_scope() const { return catch_scope_; } 104 Node* outermost_handler() const { return outermost_handler_; } 105 106 Node* node_ptr() const { return node_; } 107 108 protected: 109 using NodeGenerator0 = std::function<TNode<Object>()>; 110 using VoidGenerator0 = std::function<void()>; 111 112 // TODO(jgruber): Currently IfBuilder0 and IfBuilder1 are implemented as 113 // separate classes. If, in the future, we encounter additional use cases that 114 // return more than 1 value, we should merge these back into a single variadic 115 // implementation. 116 class IfBuilder0 final { 117 public: 118 IfBuilder0(JSGraphAssembler* gasm, TNode<Boolean> cond, bool negate_cond) 119 : gasm_(gasm), 120 cond_(cond), 121 negate_cond_(negate_cond), 122 initial_effect_(gasm->effect()), 123 initial_control_(gasm->control()) {} 124 125 IfBuilder0& ExpectTrue() { 126 DCHECK_EQ(hint_, BranchHint::kNone); 127 hint_ = BranchHint::kTrue; 128 return *this; 129 } 130 IfBuilder0& ExpectFalse() { 131 DCHECK_EQ(hint_, BranchHint::kNone); 132 hint_ = BranchHint::kFalse; 133 return *this; 134 } 135 136 IfBuilder0& Then(const VoidGenerator0& body) { 137 then_body_ = body; 138 return *this; 139 } 140 IfBuilder0& Else(const VoidGenerator0& body) { 141 else_body_ = body; 142 return *this; 143 } 144 145 ~IfBuilder0() { 146 // Ensure correct usage: effect/control must not have been modified while 147 // the IfBuilder0 instance is alive. 148 DCHECK_EQ(gasm_->effect(), initial_effect_); 149 DCHECK_EQ(gasm_->control(), initial_control_); 150 151 // Unlike IfBuilder1, this supports an empty then or else body. This is 152 // possible since the merge does not take any value inputs. 153 DCHECK(then_body_ || else_body_); 154 155 if (negate_cond_) std::swap(then_body_, else_body_); 156 157 auto if_true = (hint_ == BranchHint::kFalse) ? gasm_->MakeDeferredLabel() 158 : gasm_->MakeLabel(); 159 auto if_false = (hint_ == BranchHint::kTrue) ? gasm_->MakeDeferredLabel() 160 : gasm_->MakeLabel(); 161 auto merge = gasm_->MakeLabel(); 162 gasm_->Branch(cond_, &if_true, &if_false); 163 164 gasm_->Bind(&if_true); 165 if (then_body_) then_body_(); 166 if (gasm_->HasActiveBlock()) gasm_->Goto(&merge); 167 168 gasm_->Bind(&if_false); 169 if (else_body_) else_body_(); 170 if (gasm_->HasActiveBlock()) gasm_->Goto(&merge); 171 172 gasm_->Bind(&merge); 173 } 174 175 IfBuilder0(const IfBuilder0&) = delete; 176 IfBuilder0& operator=(const IfBuilder0&) = delete; 177 178 private: 179 JSGraphAssembler* const gasm_; 180 const TNode<Boolean> cond_; 181 const bool negate_cond_; 182 const Effect initial_effect_; 183 const Control initial_control_; 184 BranchHint hint_ = BranchHint::kNone; 185 VoidGenerator0 then_body_; 186 VoidGenerator0 else_body_; 187 }; 188 189 IfBuilder0 If(TNode<Boolean> cond) { return {this, cond, false}; } 190 IfBuilder0 IfNot(TNode<Boolean> cond) { return {this, cond, true}; } 191 192 template <typename T> 193 class IfBuilder1 { 194 using If1BodyFunction = std::function<TNode<T>()>; 195 196 public: 197 IfBuilder1(JSGraphAssembler* gasm, TNode<Boolean> cond) 198 : gasm_(gasm), cond_(cond) {} 199 200 V8_WARN_UNUSED_RESULT IfBuilder1& ExpectTrue() { 201 DCHECK_EQ(hint_, BranchHint::kNone); 202 hint_ = BranchHint::kTrue; 203 return *this; 204 } 205 206 V8_WARN_UNUSED_RESULT IfBuilder1& ExpectFalse() { 207 DCHECK_EQ(hint_, BranchHint::kNone); 208 hint_ = BranchHint::kFalse; 209 return *this; 210 } 211 212 V8_WARN_UNUSED_RESULT IfBuilder1& Then(const If1BodyFunction& body) { 213 then_body_ = body; 214 return *this; 215 } 216 V8_WARN_UNUSED_RESULT IfBuilder1& Else(const If1BodyFunction& body) { 217 else_body_ = body; 218 return *this; 219 } 220 221 V8_WARN_UNUSED_RESULT TNode<T> Value() { 222 DCHECK(then_body_); 223 DCHECK(else_body_); 224 auto if_true = (hint_ == BranchHint::kFalse) ? gasm_->MakeDeferredLabel() 225 : gasm_->MakeLabel(); 226 auto if_false = (hint_ == BranchHint::kTrue) ? gasm_->MakeDeferredLabel() 227 : gasm_->MakeLabel(); 228 auto merge = gasm_->MakeLabel(kPhiRepresentation); 229 gasm_->Branch(cond_, &if_true, &if_false); 230 231 gasm_->Bind(&if_true); 232 TNode<T> then_result = then_body_(); 233 if (gasm_->HasActiveBlock()) gasm_->Goto(&merge, then_result); 234 235 gasm_->Bind(&if_false); 236 TNode<T> else_result = else_body_(); 237 if (gasm_->HasActiveBlock()) { 238 gasm_->Goto(&merge, else_result); 239 } 240 241 gasm_->Bind(&merge); 242 return merge.PhiAt<T>(0); 243 } 244 245 private: 246 static constexpr MachineRepresentation kPhiRepresentation = 247 MachineRepresentation::kTagged; 248 249 JSGraphAssembler* const gasm_; 250 const TNode<Boolean> cond_; 251 BranchHint hint_ = BranchHint::kNone; 252 If1BodyFunction then_body_; 253 If1BodyFunction else_body_; 254 }; 255 256 template <typename T> 257 IfBuilder1<T> SelectIf(TNode<Boolean> cond) { 258 return {this, cond}; 259 } 260 261 // Simplified operators. 262 TNode<Number> SpeculativeToNumber( 263 TNode<Object> value, 264 NumberOperationHint hint = NumberOperationHint::kNumberOrOddball); 265 TNode<Smi> CheckSmi(TNode<Object> value); 266 TNode<String> CheckString(TNode<Object> value); 267 TNode<Number> CheckBounds(TNode<Number> value, TNode<Number> limit); 268 269 // Common operators. 270 TNode<Smi> TypeGuardUnsignedSmall(TNode<Object> value); 271 TNode<Object> TypeGuardNonInternal(TNode<Object> value); 272 TNode<Number> TypeGuardFixedArrayLength(TNode<Object> value); 273 TNode<Object> Call4(const Callable& callable, TNode<Context> context, 274 TNode<Object> arg0, TNode<Object> arg1, 275 TNode<Object> arg2, TNode<Object> arg3); 276 277 // Javascript operators. 278 TNode<Object> JSCall3(TNode<Object> function, TNode<Object> this_arg, 279 TNode<Object> arg0, TNode<Object> arg1, 280 TNode<Object> arg2, FrameState frame_state); 281 TNode<Object> JSCall4(TNode<Object> function, TNode<Object> this_arg, 282 TNode<Object> arg0, TNode<Object> arg1, 283 TNode<Object> arg2, TNode<Object> arg3, 284 FrameState frame_state); 285 TNode<Object> JSCallRuntime2(Runtime::FunctionId function_id, 286 TNode<Object> arg0, TNode<Object> arg1, 287 FrameState frame_state); 288 289 // Emplace a copy of the call node into the graph at current effect/control. 290 TNode<Object> CopyNode(); 291 292 // Used in special cases in which we are certain CreateArray does not throw. 293 TNode<JSArray> CreateArrayNoThrow(TNode<Object> ctor, TNode<Number> size, 294 FrameState frame_state); 295 296 TNode<JSArray> AllocateEmptyJSArray(ElementsKind kind, 297 const NativeContextRef& native_context); 298 299 TNode<Number> NumberInc(TNode<Number> value) { 300 return NumberAdd(value, OneConstant()); 301 } 302 303 void MaybeInsertMapChecks(MapInference* inference, 304 bool has_stability_dependency) { 305 // TODO(jgruber): Implement MapInference::InsertMapChecks in graph 306 // assembler. 307 if (!has_stability_dependency) { 308 Effect e = effect(); 309 inference->InsertMapChecks(jsgraph(), &e, Control{control()}, feedback()); 310 InitializeEffectControl(e, control()); 311 } 312 } 313 314 // TODO(jgruber): Currently, it's the responsibility of the developer to note 315 // which operations may throw and appropriately wrap these in a call to 316 // MayThrow (see e.g. JSCall3 and CallRuntime2). A more methodical approach 317 // would be good. 318 TNode<Object> MayThrow(const NodeGenerator0& body) { 319 TNode<Object> result = body(); 320 321 if (catch_scope()->has_handler()) { 322 // The IfException node is later merged into the outer graph. 323 // Note: AddNode is intentionally not called since effect and control 324 // should not be updated. 325 Node* if_exception = 326 graph()->NewNode(common()->IfException(), effect(), control()); 327 catch_scope()->RegisterIfExceptionNode(if_exception); 328 329 // Control resumes here. 330 AddNode(graph()->NewNode(common()->IfSuccess(), control())); 331 } 332 333 return result; 334 } 335 336 // A catch scope represents a single catch handler. The handler can be 337 // custom catch logic within the reduction itself; or a catch handler in the 338 // outside graph into which the reduction will be integrated (in this case 339 // the scope is called 'outermost'). 340 class V8_NODISCARD CatchScope { 341 private: 342 // Only used to partially construct the outermost scope. 343 explicit CatchScope(Zone* zone) : if_exception_nodes_(zone) {} 344 345 // For all inner scopes. 346 CatchScope(Zone* zone, JSCallReducerAssembler* gasm) 347 : gasm_(gasm), 348 parent_(gasm->catch_scope_), 349 has_handler_(true), 350 if_exception_nodes_(zone) { 351 gasm_->catch_scope_ = this; 352 } 353 354 public: 355 ~CatchScope() { gasm_->catch_scope_ = parent_; } 356 357 static CatchScope Outermost(Zone* zone) { return CatchScope{zone}; } 358 static CatchScope Inner(Zone* zone, JSCallReducerAssembler* gasm) { 359 return {zone, gasm}; 360 } 361 362 bool has_handler() const { return has_handler_; } 363 bool is_outermost() const { return parent_ == nullptr; } 364 CatchScope* parent() const { return parent_; } 365 366 // Should only be used to initialize the outermost scope (inner scopes 367 // always have a handler and are passed the gasm pointer at construction). 368 void set_has_handler(bool v) { 369 DCHECK(is_outermost()); 370 has_handler_ = v; 371 } 372 void set_gasm(JSCallReducerAssembler* v) { 373 DCHECK(is_outermost()); 374 gasm_ = v; 375 } 376 377 bool has_exceptional_control_flow() const { 378 return !if_exception_nodes_.empty(); 379 } 380 381 void RegisterIfExceptionNode(Node* if_exception) { 382 DCHECK(has_handler()); 383 if_exception_nodes_.push_back(if_exception); 384 } 385 386 void MergeExceptionalPaths(TNode<Object>* exception_out, Effect* effect_out, 387 Control* control_out) { 388 DCHECK(has_handler()); 389 DCHECK(has_exceptional_control_flow()); 390 391 const int size = static_cast<int>(if_exception_nodes_.size()); 392 393 if (size == 1) { 394 // No merge needed. 395 Node* e = if_exception_nodes_.at(0); 396 *exception_out = TNode<Object>::UncheckedCast(e); 397 *effect_out = Effect(e); 398 *control_out = Control(e); 399 } else { 400 DCHECK_GT(size, 1); 401 402 Node* merge = gasm_->graph()->NewNode(gasm_->common()->Merge(size), 403 size, if_exception_nodes_.data()); 404 405 // These phis additionally take {merge} as an input. Temporarily add 406 // it to the list. 407 if_exception_nodes_.push_back(merge); 408 const int size_with_merge = 409 static_cast<int>(if_exception_nodes_.size()); 410 411 Node* ephi = gasm_->graph()->NewNode(gasm_->common()->EffectPhi(size), 412 size_with_merge, 413 if_exception_nodes_.data()); 414 Node* phi = gasm_->graph()->NewNode( 415 gasm_->common()->Phi(MachineRepresentation::kTagged, size), 416 size_with_merge, if_exception_nodes_.data()); 417 if_exception_nodes_.pop_back(); 418 419 *exception_out = TNode<Object>::UncheckedCast(phi); 420 *effect_out = Effect(ephi); 421 *control_out = Control(merge); 422 } 423 } 424 425 private: 426 JSCallReducerAssembler* gasm_ = nullptr; 427 CatchScope* const parent_ = nullptr; 428 bool has_handler_ = false; 429 NodeVector if_exception_nodes_; 430 }; 431 432 class TryCatchBuilder0 { 433 public: 434 using TryFunction = VoidGenerator0; 435 using CatchFunction = std::function<void(TNode<Object>)>; 436 437 TryCatchBuilder0(JSCallReducerAssembler* gasm, const TryFunction& try_body) 438 : gasm_(gasm), try_body_(try_body) {} 439 440 void Catch(const CatchFunction& catch_body) { 441 TNode<Object> handler_exception; 442 Effect handler_effect{nullptr}; 443 Control handler_control{nullptr}; 444 445 auto continuation = gasm_->MakeLabel(); 446 447 // Try. 448 { 449 CatchScope catch_scope = CatchScope::Inner(gasm_->temp_zone(), gasm_); 450 try_body_(); 451 gasm_->Goto(&continuation); 452 453 catch_scope.MergeExceptionalPaths(&handler_exception, &handler_effect, 454 &handler_control); 455 } 456 457 // Catch. 458 { 459 gasm_->InitializeEffectControl(handler_effect, handler_control); 460 catch_body(handler_exception); 461 gasm_->Goto(&continuation); 462 } 463 464 gasm_->Bind(&continuation); 465 } 466 467 private: 468 JSCallReducerAssembler* const gasm_; 469 const VoidGenerator0 try_body_; 470 }; 471 472 TryCatchBuilder0 Try(const VoidGenerator0& try_body) { 473 return {this, try_body}; 474 } 475 476 using ConditionFunction1 = std::function<TNode<Boolean>(TNode<Number>)>; 477 using StepFunction1 = std::function<TNode<Number>(TNode<Number>)>; 478 class ForBuilder0 { 479 using For0BodyFunction = std::function<void(TNode<Number>)>; 480 481 public: 482 ForBuilder0(JSGraphAssembler* gasm, TNode<Number> initial_value, 483 const ConditionFunction1& cond, const StepFunction1& step) 484 : gasm_(gasm), 485 initial_value_(initial_value), 486 cond_(cond), 487 step_(step) {} 488 489 void Do(const For0BodyFunction& body) { 490 auto loop_exit = gasm_->MakeLabel(); 491 492 { 493 GraphAssembler::LoopScope<kPhiRepresentation> loop_scope(gasm_); 494 495 auto loop_header = loop_scope.loop_header_label(); 496 auto loop_body = gasm_->MakeLabel(); 497 498 gasm_->Goto(loop_header, initial_value_); 499 500 gasm_->Bind(loop_header); 501 TNode<Number> i = loop_header->PhiAt<Number>(0); 502 503 gasm_->BranchWithHint(cond_(i), &loop_body, &loop_exit, 504 BranchHint::kTrue); 505 506 gasm_->Bind(&loop_body); 507 body(i); 508 gasm_->Goto(loop_header, step_(i)); 509 } 510 511 gasm_->Bind(&loop_exit); 512 } 513 514 private: 515 static constexpr MachineRepresentation kPhiRepresentation = 516 MachineRepresentation::kTagged; 517 518 JSGraphAssembler* const gasm_; 519 const TNode<Number> initial_value_; 520 const ConditionFunction1 cond_; 521 const StepFunction1 step_; 522 }; 523 524 ForBuilder0 ForZeroUntil(TNode<Number> excluded_limit) { 525 TNode<Number> initial_value = ZeroConstant(); 526 auto cond = [=](TNode<Number> i) { 527 return NumberLessThan(i, excluded_limit); 528 }; 529 auto step = [=](TNode<Number> i) { return NumberAdd(i, OneConstant()); }; 530 return {this, initial_value, cond, step}; 531 } 532 533 ForBuilder0 Forever(TNode<Number> initial_value, const StepFunction1& step) { 534 return {this, initial_value, [=](TNode<Number>) { return TrueConstant(); }, 535 step}; 536 } 537 538 using For1BodyFunction = std::function<void(TNode<Number>, TNode<Object>*)>; 539 class ForBuilder1 { 540 public: 541 ForBuilder1(JSGraphAssembler* gasm, TNode<Number> initial_value, 542 const ConditionFunction1& cond, const StepFunction1& step, 543 TNode<Object> initial_arg0) 544 : gasm_(gasm), 545 initial_value_(initial_value), 546 cond_(cond), 547 step_(step), 548 initial_arg0_(initial_arg0) {} 549 550 V8_WARN_UNUSED_RESULT ForBuilder1& Do(const For1BodyFunction& body) { 551 body_ = body; 552 return *this; 553 } 554 555 V8_WARN_UNUSED_RESULT TNode<Object> Value() { 556 DCHECK(body_); 557 TNode<Object> arg0 = initial_arg0_; 558 559 auto loop_exit = gasm_->MakeDeferredLabel(kPhiRepresentation); 560 561 { 562 GraphAssembler::LoopScope<kPhiRepresentation, kPhiRepresentation> 563 loop_scope(gasm_); 564 565 auto loop_header = loop_scope.loop_header_label(); 566 auto loop_body = gasm_->MakeDeferredLabel(kPhiRepresentation); 567 568 gasm_->Goto(loop_header, initial_value_, initial_arg0_); 569 570 gasm_->Bind(loop_header); 571 TNode<Number> i = loop_header->PhiAt<Number>(0); 572 arg0 = loop_header->PhiAt<Object>(1); 573 574 gasm_->BranchWithHint(cond_(i), &loop_body, &loop_exit, 575 BranchHint::kTrue, arg0); 576 577 gasm_->Bind(&loop_body); 578 body_(i, &arg0); 579 gasm_->Goto(loop_header, step_(i), arg0); 580 } 581 582 gasm_->Bind(&loop_exit); 583 return TNode<Object>::UncheckedCast(loop_exit.PhiAt<Object>(0)); 584 } 585 586 void ValueIsUnused() { USE(Value()); } 587 588 private: 589 static constexpr MachineRepresentation kPhiRepresentation = 590 MachineRepresentation::kTagged; 591 592 JSGraphAssembler* const gasm_; 593 const TNode<Number> initial_value_; 594 const ConditionFunction1 cond_; 595 const StepFunction1 step_; 596 For1BodyFunction body_; 597 const TNode<Object> initial_arg0_; 598 }; 599 600 ForBuilder1 For1(TNode<Number> initial_value, const ConditionFunction1& cond, 601 const StepFunction1& step, TNode<Object> initial_arg0) { 602 return {this, initial_value, cond, step, initial_arg0}; 603 } 604 605 ForBuilder1 For1ZeroUntil(TNode<Number> excluded_limit, 606 TNode<Object> initial_arg0) { 607 TNode<Number> initial_value = ZeroConstant(); 608 auto cond = [=](TNode<Number> i) { 609 return NumberLessThan(i, excluded_limit); 610 }; 611 auto step = [=](TNode<Number> i) { return NumberAdd(i, OneConstant()); }; 612 return {this, initial_value, cond, step, initial_arg0}; 613 } 614 615 void ThrowIfNotCallable(TNode<Object> maybe_callable, 616 FrameState frame_state) { 617 IfNot(ObjectIsCallable(maybe_callable)) 618 .Then(_ { 619 JSCallRuntime2(Runtime::kThrowTypeError, 620 NumberConstant(static_cast<double>( 621 MessageTemplate::kCalledNonCallable)), 622 maybe_callable, frame_state); 623 Unreachable(); // The runtime call throws unconditionally. 624 }) 625 .ExpectTrue(); 626 } 627 628 const FeedbackSource& feedback() const { 629 CallParameters const& p = CallParametersOf(node_ptr()->op()); 630 return p.feedback(); 631 } 632 633 int ArgumentCount() const { return JSCallNode{node_ptr()}.ArgumentCount(); } 634 635 TNode<Object> Argument(int index) const { 636 return TNode<Object>::UncheckedCast(JSCallNode{node_ptr()}.Argument(index)); 637 } 638 639 template <typename T> 640 TNode<T> ArgumentAs(int index) const { 641 return TNode<T>::UncheckedCast(Argument(index)); 642 } 643 644 TNode<Object> ArgumentOrNaN(int index) { 645 return TNode<Object>::UncheckedCast( 646 ArgumentCount() > index ? Argument(index) : NaNConstant()); 647 } 648 649 TNode<Object> ArgumentOrUndefined(int index) { 650 return TNode<Object>::UncheckedCast( 651 ArgumentCount() > index ? Argument(index) : UndefinedConstant()); 652 } 653 654 TNode<Number> ArgumentOrZero(int index) { 655 return TNode<Number>::UncheckedCast( 656 ArgumentCount() > index ? Argument(index) : ZeroConstant()); 657 } 658 659 TNode<Context> ContextInput() const { 660 return TNode<Context>::UncheckedCast( 661 NodeProperties::GetContextInput(node_)); 662 } 663 664 FrameState FrameStateInput() const { 665 return FrameState(NodeProperties::GetFrameStateInput(node_)); 666 } 667 668 JSOperatorBuilder* javascript() const { return jsgraph()->javascript(); } 669 670 CompilationDependencies* dependencies() const { return dependencies_; } 671 672 private: 673 CompilationDependencies* const dependencies_; 674 Node* const node_; 675 CatchScope outermost_catch_scope_; 676 Node* outermost_handler_; 677 CatchScope* catch_scope_; 678 friend class CatchScope; 679}; 680 681enum class ArrayReduceDirection { kLeft, kRight }; 682enum class ArrayFindVariant { kFind, kFindIndex }; 683enum class ArrayEverySomeVariant { kEvery, kSome }; 684enum class ArrayIndexOfIncludesVariant { kIncludes, kIndexOf }; 685 686// This subclass bundles functionality specific to reducing iterating array 687// builtins. 688class IteratingArrayBuiltinReducerAssembler : public JSCallReducerAssembler { 689 public: 690 IteratingArrayBuiltinReducerAssembler(JSCallReducer* reducer, Node* node) 691 : JSCallReducerAssembler(reducer, node) { 692 DCHECK(FLAG_turbo_inline_array_builtins); 693 } 694 695 TNode<Object> ReduceArrayPrototypeForEach( 696 MapInference* inference, const bool has_stability_dependency, 697 ElementsKind kind, const SharedFunctionInfoRef& shared); 698 TNode<Object> ReduceArrayPrototypeReduce(MapInference* inference, 699 const bool has_stability_dependency, 700 ElementsKind kind, 701 ArrayReduceDirection direction, 702 const SharedFunctionInfoRef& shared); 703 TNode<JSArray> ReduceArrayPrototypeMap( 704 MapInference* inference, const bool has_stability_dependency, 705 ElementsKind kind, const SharedFunctionInfoRef& shared, 706 const NativeContextRef& native_context); 707 TNode<JSArray> ReduceArrayPrototypeFilter( 708 MapInference* inference, const bool has_stability_dependency, 709 ElementsKind kind, const SharedFunctionInfoRef& shared, 710 const NativeContextRef& native_context); 711 TNode<Object> ReduceArrayPrototypeFind(MapInference* inference, 712 const bool has_stability_dependency, 713 ElementsKind kind, 714 const SharedFunctionInfoRef& shared, 715 const NativeContextRef& native_context, 716 ArrayFindVariant variant); 717 TNode<Boolean> ReduceArrayPrototypeEverySome( 718 MapInference* inference, const bool has_stability_dependency, 719 ElementsKind kind, const SharedFunctionInfoRef& shared, 720 const NativeContextRef& native_context, ArrayEverySomeVariant variant); 721 TNode<Object> ReduceArrayPrototypeIndexOfIncludes( 722 ElementsKind kind, ArrayIndexOfIncludesVariant variant); 723 724 private: 725 // Returns {index,value}. Assumes that the map has not changed, but possibly 726 // the length and backing store. 727 std::pair<TNode<Number>, TNode<Object>> SafeLoadElement(ElementsKind kind, 728 TNode<JSArray> o, 729 TNode<Number> index) { 730 // Make sure that the access is still in bounds, since the callback could 731 // have changed the array's size. 732 TNode<Number> length = LoadJSArrayLength(o, kind); 733 index = CheckBounds(index, length); 734 735 // Reload the elements pointer before calling the callback, since the 736 // previous callback might have resized the array causing the elements 737 // buffer to be re-allocated. 738 TNode<HeapObject> elements = 739 LoadField<HeapObject>(AccessBuilder::ForJSObjectElements(), o); 740 TNode<Object> value = LoadElement<Object>( 741 AccessBuilder::ForFixedArrayElement(kind), elements, index); 742 return std::make_pair(index, value); 743 } 744 745 template <typename... Vars> 746 TNode<Object> MaybeSkipHole( 747 TNode<Object> o, ElementsKind kind, 748 GraphAssemblerLabel<sizeof...(Vars)>* continue_label, 749 TNode<Vars>... vars) { 750 if (!IsHoleyElementsKind(kind)) return o; 751 752 std::array<MachineRepresentation, sizeof...(Vars)> reps = { 753 MachineRepresentationOf<Vars>::value...}; 754 auto if_not_hole = 755 MakeLabel<sizeof...(Vars)>(reps, GraphAssemblerLabelType::kNonDeferred); 756 BranchWithHint(HoleCheck(kind, o), continue_label, &if_not_hole, 757 BranchHint::kFalse, vars...); 758 759 // The contract is that we don't leak "the hole" into "user JavaScript", 760 // so we must rename the {element} here to explicitly exclude "the hole" 761 // from the type of {element}. 762 Bind(&if_not_hole); 763 return TypeGuardNonInternal(o); 764 } 765 766 TNode<Smi> LoadJSArrayLength(TNode<JSArray> array, ElementsKind kind) { 767 return LoadField<Smi>(AccessBuilder::ForJSArrayLength(kind), array); 768 } 769 void StoreJSArrayLength(TNode<JSArray> array, TNode<Number> value, 770 ElementsKind kind) { 771 StoreField(AccessBuilder::ForJSArrayLength(kind), array, value); 772 } 773 void StoreFixedArrayBaseElement(TNode<FixedArrayBase> o, TNode<Number> index, 774 TNode<Object> v, ElementsKind kind) { 775 StoreElement(AccessBuilder::ForFixedArrayElement(kind), o, index, v); 776 } 777 778 TNode<FixedArrayBase> LoadElements(TNode<JSObject> o) { 779 return LoadField<FixedArrayBase>(AccessBuilder::ForJSObjectElements(), o); 780 } 781 TNode<Smi> LoadFixedArrayBaseLength(TNode<FixedArrayBase> o) { 782 return LoadField<Smi>(AccessBuilder::ForFixedArrayLength(), o); 783 } 784 785 TNode<Boolean> HoleCheck(ElementsKind kind, TNode<Object> v) { 786 return IsDoubleElementsKind(kind) 787 ? NumberIsFloat64Hole(TNode<Number>::UncheckedCast(v)) 788 : IsTheHole(v); 789 } 790 791 TNode<Number> CheckFloat64Hole(TNode<Number> value, 792 CheckFloat64HoleMode mode) { 793 return AddNode<Number>( 794 graph()->NewNode(simplified()->CheckFloat64Hole(mode, feedback()), 795 value, effect(), control())); 796 } 797 798 // May deopt for holey double elements. 799 TNode<Object> TryConvertHoleToUndefined(TNode<Object> value, 800 ElementsKind kind) { 801 DCHECK(IsHoleyElementsKind(kind)); 802 if (kind == HOLEY_DOUBLE_ELEMENTS) { 803 // TODO(7409): avoid deopt if not all uses of value are truncated. 804 TNode<Number> number = TNode<Number>::UncheckedCast(value); 805 return CheckFloat64Hole(number, CheckFloat64HoleMode::kAllowReturnHole); 806 } 807 808 return ConvertTaggedHoleToUndefined(value); 809 } 810}; 811 812class PromiseBuiltinReducerAssembler : public JSCallReducerAssembler { 813 public: 814 PromiseBuiltinReducerAssembler(JSCallReducer* reducer, Node* node, 815 JSHeapBroker* broker) 816 : JSCallReducerAssembler(reducer, node), broker_(broker) { 817 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); 818 } 819 820 TNode<Object> ReducePromiseConstructor( 821 const NativeContextRef& native_context); 822 823 int ConstructArity() const { 824 return JSConstructNode{node_ptr()}.ArgumentCount(); 825 } 826 827 TNode<Object> TargetInput() const { 828 return JSConstructNode{node_ptr()}.target(); 829 } 830 831 TNode<Object> NewTargetInput() const { 832 return JSConstructNode{node_ptr()}.new_target(); 833 } 834 835 private: 836 TNode<JSPromise> CreatePromise(TNode<Context> context) { 837 return AddNode<JSPromise>( 838 graph()->NewNode(javascript()->CreatePromise(), context, effect())); 839 } 840 841 TNode<Context> CreateFunctionContext(const NativeContextRef& native_context, 842 TNode<Context> outer_context, 843 int slot_count) { 844 return AddNode<Context>(graph()->NewNode( 845 javascript()->CreateFunctionContext( 846 native_context.scope_info(), 847 slot_count - Context::MIN_CONTEXT_SLOTS, FUNCTION_SCOPE), 848 outer_context, effect(), control())); 849 } 850 851 void StoreContextSlot(TNode<Context> context, size_t slot_index, 852 TNode<Object> value) { 853 StoreField(AccessBuilder::ForContextSlot(slot_index), context, value); 854 } 855 856 TNode<JSFunction> CreateClosureFromBuiltinSharedFunctionInfo( 857 SharedFunctionInfoRef shared, TNode<Context> context) { 858 DCHECK(shared.HasBuiltinId()); 859 Handle<FeedbackCell> feedback_cell = 860 isolate()->factory()->many_closures_cell(); 861 Callable const callable = 862 Builtins::CallableFor(isolate(), shared.builtin_id()); 863 CodeTRef code = MakeRef(broker_, *callable.code()); 864 return AddNode<JSFunction>(graph()->NewNode( 865 javascript()->CreateClosure(shared, code), HeapConstant(feedback_cell), 866 context, effect(), control())); 867 } 868 869 void CallPromiseExecutor(TNode<Object> executor, TNode<JSFunction> resolve, 870 TNode<JSFunction> reject, FrameState frame_state) { 871 JSConstructNode n(node_ptr()); 872 const ConstructParameters& p = n.Parameters(); 873 FeedbackSource no_feedback_source{}; 874 Node* no_feedback = UndefinedConstant(); 875 MayThrow(_ { 876 return AddNode<Object>(graph()->NewNode( 877 javascript()->Call(JSCallNode::ArityForArgc(2), p.frequency(), 878 no_feedback_source, 879 ConvertReceiverMode::kNullOrUndefined), 880 executor, UndefinedConstant(), resolve, reject, no_feedback, 881 n.context(), frame_state, effect(), control())); 882 }); 883 } 884 885 void CallPromiseReject(TNode<JSFunction> reject, TNode<Object> exception, 886 FrameState frame_state) { 887 JSConstructNode n(node_ptr()); 888 const ConstructParameters& p = n.Parameters(); 889 FeedbackSource no_feedback_source{}; 890 Node* no_feedback = UndefinedConstant(); 891 MayThrow(_ { 892 return AddNode<Object>(graph()->NewNode( 893 javascript()->Call(JSCallNode::ArityForArgc(1), p.frequency(), 894 no_feedback_source, 895 ConvertReceiverMode::kNullOrUndefined), 896 reject, UndefinedConstant(), exception, no_feedback, n.context(), 897 frame_state, effect(), control())); 898 }); 899 } 900 901 JSHeapBroker* const broker_; 902}; 903 904class FastApiCallReducerAssembler : public JSCallReducerAssembler { 905 public: 906 FastApiCallReducerAssembler( 907 JSCallReducer* reducer, Node* node, 908 const FunctionTemplateInfoRef function_template_info, 909 const FastApiCallFunctionVector& c_candidate_functions, Node* receiver, 910 Node* holder, const SharedFunctionInfoRef shared, Node* target, 911 const int arity, Node* effect) 912 : JSCallReducerAssembler(reducer, node), 913 c_candidate_functions_(c_candidate_functions), 914 function_template_info_(function_template_info), 915 receiver_(receiver), 916 holder_(holder), 917 shared_(shared), 918 target_(target), 919 arity_(arity) { 920 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 921 CHECK_GT(c_candidate_functions.size(), 0); 922 InitializeEffectControl(effect, NodeProperties::GetControlInput(node)); 923 } 924 925 TNode<Object> ReduceFastApiCall() { 926 JSCallNode n(node_ptr()); 927 928 // C arguments include the receiver at index 0. Thus C index 1 corresponds 929 // to the JS argument 0, etc. 930 // All functions in c_candidate_functions_ have the same number of 931 // arguments, so extract c_argument_count from the first function. 932 const int c_argument_count = 933 static_cast<int>(c_candidate_functions_[0].signature->ArgumentCount()); 934 CHECK_GE(c_argument_count, kReceiver); 935 936 int cursor = 0; 937 base::SmallVector<Node*, kInlineSize> inputs(c_argument_count + arity_ + 938 kExtraInputsCount); 939 inputs[cursor++] = n.receiver(); 940 941 // TODO(turbofan): Consider refactoring CFunctionInfo to distinguish 942 // between receiver and arguments, simplifying this (and related) spots. 943 int js_args_count = c_argument_count - kReceiver; 944 for (int i = 0; i < js_args_count; ++i) { 945 if (i < n.ArgumentCount()) { 946 inputs[cursor++] = n.Argument(i); 947 } else { 948 inputs[cursor++] = UndefinedConstant(); 949 } 950 } 951 952 // Here we add the arguments for the slow call, which will be 953 // reconstructed at a later phase. Those are effectively the same 954 // arguments as for the fast call, but we want to have them as 955 // separate inputs, so that SimplifiedLowering can provide the best 956 // possible UseInfos for each of them. The inputs to FastApiCall 957 // look like: 958 // [fast callee, receiver, ... C arguments, 959 // call code, external constant for function, argc, call handler info data, 960 // holder, receiver, ... JS arguments, context, new frame state] 961 CallHandlerInfoRef call_handler_info = *function_template_info_.call_code(); 962 Callable call_api_callback = CodeFactory::CallApiCallback(isolate()); 963 CallInterfaceDescriptor cid = call_api_callback.descriptor(); 964 CallDescriptor* call_descriptor = 965 Linkage::GetStubCallDescriptor(graph()->zone(), cid, arity_ + kReceiver, 966 CallDescriptor::kNeedsFrameState); 967 ApiFunction api_function(call_handler_info.callback()); 968 ExternalReference function_reference = ExternalReference::Create( 969 isolate(), &api_function, ExternalReference::DIRECT_API_CALL, 970 function_template_info_.c_functions().data(), 971 function_template_info_.c_signatures().data(), 972 static_cast<unsigned>(function_template_info_.c_functions().size())); 973 974 Node* continuation_frame_state = 975 CreateGenericLazyDeoptContinuationFrameState( 976 jsgraph(), shared_, target_, ContextInput(), receiver_, 977 FrameStateInput()); 978 979 inputs[cursor++] = HeapConstant(call_api_callback.code()); 980 inputs[cursor++] = ExternalConstant(function_reference); 981 inputs[cursor++] = NumberConstant(arity_); 982 inputs[cursor++] = Constant(call_handler_info.data()); 983 inputs[cursor++] = holder_; 984 inputs[cursor++] = receiver_; 985 for (int i = 0; i < arity_; ++i) { 986 inputs[cursor++] = Argument(i); 987 } 988 inputs[cursor++] = ContextInput(); 989 inputs[cursor++] = continuation_frame_state; 990 inputs[cursor++] = effect(); 991 inputs[cursor++] = control(); 992 993 DCHECK_EQ(cursor, c_argument_count + arity_ + kExtraInputsCount); 994 995 return FastApiCall(call_descriptor, inputs.begin(), inputs.size()); 996 } 997 998 private: 999 static constexpr int kSlowTarget = 1; 1000 static constexpr int kEffectAndControl = 2; 1001 static constexpr int kContextAndFrameState = 2; 1002 static constexpr int kCallCodeDataAndArgc = 3; 1003 static constexpr int kHolder = 1, kReceiver = 1; 1004 static constexpr int kExtraInputsCount = 1005 kSlowTarget + kEffectAndControl + kContextAndFrameState + 1006 kCallCodeDataAndArgc + kHolder + kReceiver; 1007 static constexpr int kInlineSize = 12; 1008 1009 TNode<Object> FastApiCall(CallDescriptor* descriptor, Node** inputs, 1010 size_t inputs_size) { 1011 return AddNode<Object>( 1012 graph()->NewNode(simplified()->FastApiCall(c_candidate_functions_, 1013 feedback(), descriptor), 1014 static_cast<int>(inputs_size), inputs)); 1015 } 1016 1017 const FastApiCallFunctionVector c_candidate_functions_; 1018 const FunctionTemplateInfoRef function_template_info_; 1019 Node* const receiver_; 1020 Node* const holder_; 1021 const SharedFunctionInfoRef shared_; 1022 Node* const target_; 1023 const int arity_; 1024}; 1025 1026TNode<Number> JSCallReducerAssembler::SpeculativeToNumber( 1027 TNode<Object> value, NumberOperationHint hint) { 1028 return AddNode<Number>( 1029 graph()->NewNode(simplified()->SpeculativeToNumber(hint, feedback()), 1030 value, effect(), control())); 1031} 1032 1033TNode<Smi> JSCallReducerAssembler::CheckSmi(TNode<Object> value) { 1034 return AddNode<Smi>(graph()->NewNode(simplified()->CheckSmi(feedback()), 1035 value, effect(), control())); 1036} 1037 1038TNode<String> JSCallReducerAssembler::CheckString(TNode<Object> value) { 1039 return AddNode<String>(graph()->NewNode(simplified()->CheckString(feedback()), 1040 value, effect(), control())); 1041} 1042 1043TNode<Number> JSCallReducerAssembler::CheckBounds(TNode<Number> value, 1044 TNode<Number> limit) { 1045 return AddNode<Number>(graph()->NewNode(simplified()->CheckBounds(feedback()), 1046 value, limit, effect(), control())); 1047} 1048 1049TNode<Smi> JSCallReducerAssembler::TypeGuardUnsignedSmall(TNode<Object> value) { 1050 return TNode<Smi>::UncheckedCast(TypeGuard(Type::UnsignedSmall(), value)); 1051} 1052 1053TNode<Object> JSCallReducerAssembler::TypeGuardNonInternal( 1054 TNode<Object> value) { 1055 return TNode<Object>::UncheckedCast(TypeGuard(Type::NonInternal(), value)); 1056} 1057 1058TNode<Number> JSCallReducerAssembler::TypeGuardFixedArrayLength( 1059 TNode<Object> value) { 1060 DCHECK(TypeCache::Get()->kFixedDoubleArrayLengthType.Is( 1061 TypeCache::Get()->kFixedArrayLengthType)); 1062 return TNode<Number>::UncheckedCast( 1063 TypeGuard(TypeCache::Get()->kFixedArrayLengthType, value)); 1064} 1065 1066TNode<Object> JSCallReducerAssembler::Call4( 1067 const Callable& callable, TNode<Context> context, TNode<Object> arg0, 1068 TNode<Object> arg1, TNode<Object> arg2, TNode<Object> arg3) { 1069 // TODO(jgruber): Make this more generic. Currently it's fitted to its single 1070 // callsite. 1071 CallDescriptor* desc = Linkage::GetStubCallDescriptor( 1072 graph()->zone(), callable.descriptor(), 1073 callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags, 1074 Operator::kEliminatable); 1075 1076 return TNode<Object>::UncheckedCast(Call(desc, HeapConstant(callable.code()), 1077 arg0, arg1, arg2, arg3, context)); 1078} 1079 1080TNode<Object> JSCallReducerAssembler::JSCall3( 1081 TNode<Object> function, TNode<Object> this_arg, TNode<Object> arg0, 1082 TNode<Object> arg1, TNode<Object> arg2, FrameState frame_state) { 1083 JSCallNode n(node_ptr()); 1084 CallParameters const& p = n.Parameters(); 1085 return MayThrow(_ { 1086 return AddNode<Object>(graph()->NewNode( 1087 javascript()->Call(JSCallNode::ArityForArgc(3), p.frequency(), 1088 p.feedback(), ConvertReceiverMode::kAny, 1089 p.speculation_mode(), 1090 CallFeedbackRelation::kUnrelated), 1091 function, this_arg, arg0, arg1, arg2, n.feedback_vector(), 1092 ContextInput(), frame_state, effect(), control())); 1093 }); 1094} 1095 1096TNode<Object> JSCallReducerAssembler::JSCall4( 1097 TNode<Object> function, TNode<Object> this_arg, TNode<Object> arg0, 1098 TNode<Object> arg1, TNode<Object> arg2, TNode<Object> arg3, 1099 FrameState frame_state) { 1100 JSCallNode n(node_ptr()); 1101 CallParameters const& p = n.Parameters(); 1102 return MayThrow(_ { 1103 return AddNode<Object>(graph()->NewNode( 1104 javascript()->Call(JSCallNode::ArityForArgc(4), p.frequency(), 1105 p.feedback(), ConvertReceiverMode::kAny, 1106 p.speculation_mode(), 1107 CallFeedbackRelation::kUnrelated), 1108 function, this_arg, arg0, arg1, arg2, arg3, n.feedback_vector(), 1109 ContextInput(), frame_state, effect(), control())); 1110 }); 1111} 1112 1113TNode<Object> JSCallReducerAssembler::JSCallRuntime2( 1114 Runtime::FunctionId function_id, TNode<Object> arg0, TNode<Object> arg1, 1115 FrameState frame_state) { 1116 return MayThrow(_ { 1117 return AddNode<Object>( 1118 graph()->NewNode(javascript()->CallRuntime(function_id, 2), arg0, arg1, 1119 ContextInput(), frame_state, effect(), control())); 1120 }); 1121} 1122 1123TNode<Object> JSCallReducerAssembler::CopyNode() { 1124 return MayThrow(_ { 1125 Node* copy = graph()->CloneNode(node_ptr()); 1126 NodeProperties::ReplaceEffectInput(copy, effect()); 1127 NodeProperties::ReplaceControlInput(copy, control()); 1128 return AddNode<Object>(copy); 1129 }); 1130} 1131 1132TNode<JSArray> JSCallReducerAssembler::CreateArrayNoThrow( 1133 TNode<Object> ctor, TNode<Number> size, FrameState frame_state) { 1134 return AddNode<JSArray>( 1135 graph()->NewNode(javascript()->CreateArray(1, base::nullopt), ctor, ctor, 1136 size, ContextInput(), frame_state, effect(), control())); 1137} 1138TNode<JSArray> JSCallReducerAssembler::AllocateEmptyJSArray( 1139 ElementsKind kind, const NativeContextRef& native_context) { 1140 // TODO(jgruber): Port AllocationBuilder to JSGraphAssembler. 1141 MapRef map = native_context.GetInitialJSArrayMap(kind); 1142 1143 AllocationBuilder ab(jsgraph(), effect(), control()); 1144 ab.Allocate(map.instance_size(), AllocationType::kYoung, Type::Array()); 1145 ab.Store(AccessBuilder::ForMap(), map); 1146 Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant(); 1147 ab.Store(AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer(), 1148 empty_fixed_array); 1149 ab.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array); 1150 ab.Store(AccessBuilder::ForJSArrayLength(kind), jsgraph()->ZeroConstant()); 1151 for (int i = 0; i < map.GetInObjectProperties(); ++i) { 1152 ab.Store(AccessBuilder::ForJSObjectInObjectProperty(map, i), 1153 jsgraph()->UndefinedConstant()); 1154 } 1155 Node* result = ab.Finish(); 1156 InitializeEffectControl(result, control()); 1157 return TNode<JSArray>::UncheckedCast(result); 1158} 1159 1160TNode<Object> JSCallReducerAssembler::ReduceMathUnary(const Operator* op) { 1161 TNode<Object> input = Argument(0); 1162 TNode<Number> input_as_number = SpeculativeToNumber(input); 1163 return TNode<Object>::UncheckedCast(graph()->NewNode(op, input_as_number)); 1164} 1165 1166TNode<Object> JSCallReducerAssembler::ReduceMathBinary(const Operator* op) { 1167 TNode<Object> left = Argument(0); 1168 TNode<Object> right = ArgumentOrNaN(1); 1169 TNode<Number> left_number = SpeculativeToNumber(left); 1170 TNode<Number> right_number = SpeculativeToNumber(right); 1171 return TNode<Object>::UncheckedCast( 1172 graph()->NewNode(op, left_number, right_number)); 1173} 1174 1175TNode<String> JSCallReducerAssembler::ReduceStringPrototypeSubstring() { 1176 TNode<Object> receiver = ReceiverInput(); 1177 TNode<Object> start = Argument(0); 1178 TNode<Object> end = ArgumentOrUndefined(1); 1179 1180 TNode<String> receiver_string = CheckString(receiver); 1181 TNode<Number> start_smi = CheckSmi(start); 1182 1183 TNode<Number> length = StringLength(receiver_string); 1184 1185 TNode<Number> end_smi = SelectIf<Number>(IsUndefined(end)) 1186 .Then(_ { return length; }) 1187 .Else(_ { return CheckSmi(end); }) 1188 .ExpectFalse() 1189 .Value(); 1190 1191 TNode<Number> zero = TNode<Number>::UncheckedCast(ZeroConstant()); 1192 TNode<Number> finalStart = NumberMin(NumberMax(start_smi, zero), length); 1193 TNode<Number> finalEnd = NumberMin(NumberMax(end_smi, zero), length); 1194 TNode<Number> from = NumberMin(finalStart, finalEnd); 1195 TNode<Number> to = NumberMax(finalStart, finalEnd); 1196 1197 return StringSubstring(receiver_string, from, to); 1198} 1199 1200TNode<Boolean> JSCallReducerAssembler::ReduceStringPrototypeStartsWith( 1201 const StringRef& search_element_string) { 1202 TNode<Object> receiver = ReceiverInput(); 1203 TNode<Object> start = ArgumentOrZero(1); 1204 1205 TNode<String> receiver_string = CheckString(receiver); 1206 TNode<Smi> start_smi = CheckSmi(start); 1207 TNode<Number> length = StringLength(receiver_string); 1208 1209 TNode<Number> zero = ZeroConstant(); 1210 TNode<Number> clamped_start = NumberMin(NumberMax(start_smi, zero), length); 1211 1212 int search_string_length = search_element_string.length().value(); 1213 DCHECK(search_string_length <= JSCallReducer::kMaxInlineMatchSequence); 1214 1215 auto out = MakeLabel(MachineRepresentation::kTagged); 1216 1217 auto search_string_too_long = 1218 NumberLessThan(NumberSubtract(length, clamped_start), 1219 NumberConstant(search_string_length)); 1220 1221 GotoIf(search_string_too_long, &out, BranchHint::kFalse, FalseConstant()); 1222 1223 STATIC_ASSERT(String::kMaxLength <= kSmiMaxValue); 1224 1225 for (int i = 0; i < search_string_length; i++) { 1226 TNode<Number> k = NumberConstant(i); 1227 TNode<Number> receiver_string_position = TNode<Number>::UncheckedCast( 1228 TypeGuard(Type::UnsignedSmall(), NumberAdd(k, clamped_start))); 1229 Node* receiver_string_char = 1230 StringCharCodeAt(receiver_string, receiver_string_position); 1231 Node* search_string_char = 1232 jsgraph()->Constant(search_element_string.GetChar(i).value()); 1233 auto is_equal = graph()->NewNode(simplified()->NumberEqual(), 1234 search_string_char, receiver_string_char); 1235 GotoIfNot(is_equal, &out, FalseConstant()); 1236 } 1237 1238 Goto(&out, TrueConstant()); 1239 1240 Bind(&out); 1241 return out.PhiAt<Boolean>(0); 1242} 1243 1244TNode<Boolean> JSCallReducerAssembler::ReduceStringPrototypeStartsWith() { 1245 TNode<Object> receiver = ReceiverInput(); 1246 TNode<Object> search_element = ArgumentOrUndefined(0); 1247 TNode<Object> start = ArgumentOrZero(1); 1248 1249 TNode<String> receiver_string = CheckString(receiver); 1250 TNode<String> search_string = CheckString(search_element); 1251 TNode<Smi> start_smi = CheckSmi(start); 1252 TNode<Number> length = StringLength(receiver_string); 1253 1254 TNode<Number> zero = ZeroConstant(); 1255 TNode<Number> clamped_start = NumberMin(NumberMax(start_smi, zero), length); 1256 1257 TNode<Number> search_string_length = StringLength(search_string); 1258 1259 auto out = MakeLabel(MachineRepresentation::kTagged); 1260 1261 auto search_string_too_long = NumberLessThan( 1262 NumberSubtract(length, clamped_start), search_string_length); 1263 1264 GotoIf(search_string_too_long, &out, BranchHint::kFalse, FalseConstant()); 1265 1266 STATIC_ASSERT(String::kMaxLength <= kSmiMaxValue); 1267 1268 ForZeroUntil(search_string_length).Do([&](TNode<Number> k) { 1269 TNode<Number> receiver_string_position = TNode<Number>::UncheckedCast( 1270 TypeGuard(Type::UnsignedSmall(), NumberAdd(k, clamped_start))); 1271 Node* receiver_string_char = 1272 StringCharCodeAt(receiver_string, receiver_string_position); 1273 Node* search_string_char = StringCharCodeAt(search_string, k); 1274 auto is_equal = graph()->NewNode(simplified()->NumberEqual(), 1275 receiver_string_char, search_string_char); 1276 GotoIfNot(is_equal, &out, FalseConstant()); 1277 }); 1278 1279 Goto(&out, TrueConstant()); 1280 1281 Bind(&out); 1282 return out.PhiAt<Boolean>(0); 1283} 1284 1285TNode<String> JSCallReducerAssembler::ReduceStringPrototypeSlice() { 1286 TNode<Object> receiver = ReceiverInput(); 1287 TNode<Object> start = Argument(0); 1288 TNode<Object> end = ArgumentOrUndefined(1); 1289 1290 TNode<String> receiver_string = CheckString(receiver); 1291 TNode<Number> start_smi = CheckSmi(start); 1292 1293 TNode<Number> length = StringLength(receiver_string); 1294 1295 TNode<Number> end_smi = SelectIf<Number>(IsUndefined(end)) 1296 .Then(_ { return length; }) 1297 .Else(_ { return CheckSmi(end); }) 1298 .ExpectFalse() 1299 .Value(); 1300 1301 TNode<Number> zero = TNode<Number>::UncheckedCast(ZeroConstant()); 1302 TNode<Number> from_untyped = 1303 SelectIf<Number>(NumberLessThan(start_smi, zero)) 1304 .Then(_ { return NumberMax(NumberAdd(length, start_smi), zero); }) 1305 .Else(_ { return NumberMin(start_smi, length); }) 1306 .ExpectFalse() 1307 .Value(); 1308 // {from} is always in non-negative Smi range, but our typer cannot figure 1309 // that out yet. 1310 TNode<Smi> from = TypeGuardUnsignedSmall(from_untyped); 1311 1312 TNode<Number> to_untyped = 1313 SelectIf<Number>(NumberLessThan(end_smi, zero)) 1314 .Then(_ { return NumberMax(NumberAdd(length, end_smi), zero); }) 1315 .Else(_ { return NumberMin(end_smi, length); }) 1316 .ExpectFalse() 1317 .Value(); 1318 // {to} is always in non-negative Smi range, but our typer cannot figure that 1319 // out yet. 1320 TNode<Smi> to = TypeGuardUnsignedSmall(to_untyped); 1321 1322 return SelectIf<String>(NumberLessThan(from, to)) 1323 .Then(_ { return StringSubstring(receiver_string, from, to); }) 1324 .Else(_ { return EmptyStringConstant(); }) 1325 .ExpectTrue() 1326 .Value(); 1327} 1328 1329namespace { 1330 1331struct ForEachFrameStateParams { 1332 JSGraph* jsgraph; 1333 SharedFunctionInfoRef shared; 1334 TNode<Context> context; 1335 TNode<Object> target; 1336 FrameState outer_frame_state; 1337 TNode<Object> receiver; 1338 TNode<Object> callback; 1339 TNode<Object> this_arg; 1340 TNode<Object> original_length; 1341}; 1342 1343FrameState ForEachLoopLazyFrameState(const ForEachFrameStateParams& params, 1344 TNode<Object> k) { 1345 Builtin builtin = Builtin::kArrayForEachLoopLazyDeoptContinuation; 1346 Node* checkpoint_params[] = {params.receiver, params.callback, 1347 params.this_arg, k, params.original_length}; 1348 return CreateJavaScriptBuiltinContinuationFrameState( 1349 params.jsgraph, params.shared, builtin, params.target, params.context, 1350 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1351 ContinuationFrameStateMode::LAZY); 1352} 1353 1354FrameState ForEachLoopEagerFrameState(const ForEachFrameStateParams& params, 1355 TNode<Object> k) { 1356 Builtin builtin = Builtin::kArrayForEachLoopEagerDeoptContinuation; 1357 Node* checkpoint_params[] = {params.receiver, params.callback, 1358 params.this_arg, k, params.original_length}; 1359 return CreateJavaScriptBuiltinContinuationFrameState( 1360 params.jsgraph, params.shared, builtin, params.target, params.context, 1361 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1362 ContinuationFrameStateMode::EAGER); 1363} 1364 1365} // namespace 1366 1367TNode<Object> 1368IteratingArrayBuiltinReducerAssembler::ReduceArrayPrototypeForEach( 1369 MapInference* inference, const bool has_stability_dependency, 1370 ElementsKind kind, const SharedFunctionInfoRef& shared) { 1371 FrameState outer_frame_state = FrameStateInput(); 1372 TNode<Context> context = ContextInput(); 1373 TNode<Object> target = TargetInput(); 1374 TNode<JSArray> receiver = ReceiverInputAs<JSArray>(); 1375 TNode<Object> fncallback = ArgumentOrUndefined(0); 1376 TNode<Object> this_arg = ArgumentOrUndefined(1); 1377 1378 TNode<Number> original_length = LoadJSArrayLength(receiver, kind); 1379 1380 ForEachFrameStateParams frame_state_params{ 1381 jsgraph(), shared, context, target, outer_frame_state, 1382 receiver, fncallback, this_arg, original_length}; 1383 1384 ThrowIfNotCallable(fncallback, ForEachLoopLazyFrameState(frame_state_params, 1385 ZeroConstant())); 1386 1387 ForZeroUntil(original_length).Do([&](TNode<Number> k) { 1388 Checkpoint(ForEachLoopEagerFrameState(frame_state_params, k)); 1389 1390 // Deopt if the map has changed during the iteration. 1391 MaybeInsertMapChecks(inference, has_stability_dependency); 1392 1393 TNode<Object> element; 1394 std::tie(k, element) = SafeLoadElement(kind, receiver, k); 1395 1396 auto continue_label = MakeLabel(); 1397 element = MaybeSkipHole(element, kind, &continue_label); 1398 1399 TNode<Number> next_k = NumberAdd(k, OneConstant()); 1400 JSCall3(fncallback, this_arg, element, k, receiver, 1401 ForEachLoopLazyFrameState(frame_state_params, next_k)); 1402 1403 Goto(&continue_label); 1404 Bind(&continue_label); 1405 }); 1406 1407 return UndefinedConstant(); 1408} 1409 1410namespace { 1411 1412struct ReduceFrameStateParams { 1413 JSGraph* jsgraph; 1414 SharedFunctionInfoRef shared; 1415 ArrayReduceDirection direction; 1416 TNode<Context> context; 1417 TNode<Object> target; 1418 FrameState outer_frame_state; 1419}; 1420 1421FrameState ReducePreLoopLazyFrameState(const ReduceFrameStateParams& params, 1422 TNode<Object> receiver, 1423 TNode<Object> callback, TNode<Object> k, 1424 TNode<Number> original_length) { 1425 Builtin builtin = (params.direction == ArrayReduceDirection::kLeft) 1426 ? Builtin::kArrayReduceLoopLazyDeoptContinuation 1427 : Builtin::kArrayReduceRightLoopLazyDeoptContinuation; 1428 Node* checkpoint_params[] = {receiver, callback, k, original_length}; 1429 return CreateJavaScriptBuiltinContinuationFrameState( 1430 params.jsgraph, params.shared, builtin, params.target, params.context, 1431 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1432 ContinuationFrameStateMode::LAZY); 1433} 1434 1435FrameState ReducePreLoopEagerFrameState(const ReduceFrameStateParams& params, 1436 TNode<Object> receiver, 1437 TNode<Object> callback, 1438 TNode<Number> original_length) { 1439 Builtin builtin = 1440 (params.direction == ArrayReduceDirection::kLeft) 1441 ? Builtin::kArrayReducePreLoopEagerDeoptContinuation 1442 : Builtin::kArrayReduceRightPreLoopEagerDeoptContinuation; 1443 Node* checkpoint_params[] = {receiver, callback, original_length}; 1444 return CreateJavaScriptBuiltinContinuationFrameState( 1445 params.jsgraph, params.shared, builtin, params.target, params.context, 1446 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1447 ContinuationFrameStateMode::EAGER); 1448} 1449 1450FrameState ReduceLoopLazyFrameState(const ReduceFrameStateParams& params, 1451 TNode<Object> receiver, 1452 TNode<Object> callback, TNode<Object> k, 1453 TNode<Number> original_length) { 1454 Builtin builtin = (params.direction == ArrayReduceDirection::kLeft) 1455 ? Builtin::kArrayReduceLoopLazyDeoptContinuation 1456 : Builtin::kArrayReduceRightLoopLazyDeoptContinuation; 1457 Node* checkpoint_params[] = {receiver, callback, k, original_length}; 1458 return CreateJavaScriptBuiltinContinuationFrameState( 1459 params.jsgraph, params.shared, builtin, params.target, params.context, 1460 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1461 ContinuationFrameStateMode::LAZY); 1462} 1463 1464FrameState ReduceLoopEagerFrameState(const ReduceFrameStateParams& params, 1465 TNode<Object> receiver, 1466 TNode<Object> callback, TNode<Object> k, 1467 TNode<Number> original_length, 1468 TNode<Object> accumulator) { 1469 Builtin builtin = (params.direction == ArrayReduceDirection::kLeft) 1470 ? Builtin::kArrayReduceLoopEagerDeoptContinuation 1471 : Builtin::kArrayReduceRightLoopEagerDeoptContinuation; 1472 Node* checkpoint_params[] = {receiver, callback, k, original_length, 1473 accumulator}; 1474 return CreateJavaScriptBuiltinContinuationFrameState( 1475 params.jsgraph, params.shared, builtin, params.target, params.context, 1476 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1477 ContinuationFrameStateMode::EAGER); 1478} 1479 1480} // namespace 1481 1482TNode<Object> IteratingArrayBuiltinReducerAssembler::ReduceArrayPrototypeReduce( 1483 MapInference* inference, const bool has_stability_dependency, 1484 ElementsKind kind, ArrayReduceDirection direction, 1485 const SharedFunctionInfoRef& shared) { 1486 FrameState outer_frame_state = FrameStateInput(); 1487 TNode<Context> context = ContextInput(); 1488 TNode<Object> target = TargetInput(); 1489 TNode<JSArray> receiver = ReceiverInputAs<JSArray>(); 1490 TNode<Object> fncallback = ArgumentOrUndefined(0); 1491 1492 ReduceFrameStateParams frame_state_params{ 1493 jsgraph(), shared, direction, context, target, outer_frame_state}; 1494 1495 TNode<Number> original_length = LoadJSArrayLength(receiver, kind); 1496 1497 // Set up variable behavior depending on the reduction kind (left/right). 1498 TNode<Number> k; 1499 StepFunction1 step; 1500 ConditionFunction1 cond; 1501 TNode<Number> zero = ZeroConstant(); 1502 TNode<Number> one = OneConstant(); 1503 if (direction == ArrayReduceDirection::kLeft) { 1504 k = zero; 1505 step = [&](TNode<Number> i) { return NumberAdd(i, one); }; 1506 cond = [&](TNode<Number> i) { return NumberLessThan(i, original_length); }; 1507 } else { 1508 k = NumberSubtract(original_length, one); 1509 step = [&](TNode<Number> i) { return NumberSubtract(i, one); }; 1510 cond = [&](TNode<Number> i) { return NumberLessThanOrEqual(zero, i); }; 1511 } 1512 1513 ThrowIfNotCallable( 1514 fncallback, ReducePreLoopLazyFrameState(frame_state_params, receiver, 1515 fncallback, k, original_length)); 1516 1517 // Set initial accumulator value. 1518 TNode<Object> accumulator; 1519 if (ArgumentCount() > 1) { 1520 accumulator = Argument(1); // Initial value specified by the user. 1521 } else { 1522 // The initial value was not specified by the user. In this case, the first 1523 // (or last in the case of reduceRight) non-holey value of the array is 1524 // used. Loop until we find it. If not found, trigger a deopt. 1525 // TODO(jgruber): The deopt does not seem necessary. Instead we could simply 1526 // throw the TypeError here from optimized code. 1527 auto found_initial_element = MakeLabel(MachineRepresentation::kTagged, 1528 MachineRepresentation::kTagged); 1529 Forever(k, step).Do([&](TNode<Number> k) { 1530 Checkpoint(ReducePreLoopEagerFrameState(frame_state_params, receiver, 1531 fncallback, original_length)); 1532 CheckIf(cond(k), DeoptimizeReason::kNoInitialElement); 1533 1534 TNode<Object> element; 1535 std::tie(k, element) = SafeLoadElement(kind, receiver, k); 1536 1537 auto continue_label = MakeLabel(); 1538 GotoIf(HoleCheck(kind, element), &continue_label); 1539 Goto(&found_initial_element, k, TypeGuardNonInternal(element)); 1540 1541 Bind(&continue_label); 1542 }); 1543 Unreachable(); // The loop is exited either by deopt or a jump to below. 1544 1545 // TODO(jgruber): This manual fiddling with blocks could be avoided by 1546 // implementing a `break` mechanic for loop builders. 1547 Bind(&found_initial_element); 1548 k = step(found_initial_element.PhiAt<Number>(0)); 1549 accumulator = found_initial_element.PhiAt<Object>(1); 1550 } 1551 1552 TNode<Object> result = 1553 For1(k, cond, step, accumulator) 1554 .Do([&](TNode<Number> k, TNode<Object>* accumulator) { 1555 Checkpoint(ReduceLoopEagerFrameState(frame_state_params, receiver, 1556 fncallback, k, original_length, 1557 *accumulator)); 1558 1559 // Deopt if the map has changed during the iteration. 1560 MaybeInsertMapChecks(inference, has_stability_dependency); 1561 1562 TNode<Object> element; 1563 std::tie(k, element) = SafeLoadElement(kind, receiver, k); 1564 1565 auto continue_label = MakeLabel(MachineRepresentation::kTagged); 1566 element = 1567 MaybeSkipHole(element, kind, &continue_label, *accumulator); 1568 1569 TNode<Number> next_k = step(k); 1570 TNode<Object> next_accumulator = JSCall4( 1571 fncallback, UndefinedConstant(), *accumulator, element, k, 1572 receiver, 1573 ReduceLoopLazyFrameState(frame_state_params, receiver, 1574 fncallback, next_k, original_length)); 1575 Goto(&continue_label, next_accumulator); 1576 1577 Bind(&continue_label); 1578 *accumulator = continue_label.PhiAt<Object>(0); 1579 }) 1580 .Value(); 1581 1582 return result; 1583} 1584 1585namespace { 1586 1587struct MapFrameStateParams { 1588 JSGraph* jsgraph; 1589 SharedFunctionInfoRef shared; 1590 TNode<Context> context; 1591 TNode<Object> target; 1592 FrameState outer_frame_state; 1593 TNode<Object> receiver; 1594 TNode<Object> callback; 1595 TNode<Object> this_arg; 1596 base::Optional<TNode<JSArray>> a; 1597 TNode<Object> original_length; 1598}; 1599 1600FrameState MapPreLoopLazyFrameState(const MapFrameStateParams& params) { 1601 DCHECK(!params.a); 1602 Node* checkpoint_params[] = {params.receiver, params.callback, 1603 params.this_arg, params.original_length}; 1604 return CreateJavaScriptBuiltinContinuationFrameState( 1605 params.jsgraph, params.shared, 1606 Builtin::kArrayMapPreLoopLazyDeoptContinuation, params.target, 1607 params.context, checkpoint_params, arraysize(checkpoint_params), 1608 params.outer_frame_state, ContinuationFrameStateMode::LAZY); 1609} 1610 1611FrameState MapLoopLazyFrameState(const MapFrameStateParams& params, 1612 TNode<Number> k) { 1613 Node* checkpoint_params[] = { 1614 params.receiver, params.callback, params.this_arg, *params.a, k, 1615 params.original_length}; 1616 return CreateJavaScriptBuiltinContinuationFrameState( 1617 params.jsgraph, params.shared, 1618 Builtin::kArrayMapLoopLazyDeoptContinuation, params.target, 1619 params.context, checkpoint_params, arraysize(checkpoint_params), 1620 params.outer_frame_state, ContinuationFrameStateMode::LAZY); 1621} 1622 1623FrameState MapLoopEagerFrameState(const MapFrameStateParams& params, 1624 TNode<Number> k) { 1625 Node* checkpoint_params[] = { 1626 params.receiver, params.callback, params.this_arg, *params.a, k, 1627 params.original_length}; 1628 return CreateJavaScriptBuiltinContinuationFrameState( 1629 params.jsgraph, params.shared, 1630 Builtin::kArrayMapLoopEagerDeoptContinuation, params.target, 1631 params.context, checkpoint_params, arraysize(checkpoint_params), 1632 params.outer_frame_state, ContinuationFrameStateMode::EAGER); 1633} 1634 1635} // namespace 1636 1637TNode<JSArray> IteratingArrayBuiltinReducerAssembler::ReduceArrayPrototypeMap( 1638 MapInference* inference, const bool has_stability_dependency, 1639 ElementsKind kind, const SharedFunctionInfoRef& shared, 1640 const NativeContextRef& native_context) { 1641 FrameState outer_frame_state = FrameStateInput(); 1642 TNode<Context> context = ContextInput(); 1643 TNode<Object> target = TargetInput(); 1644 TNode<JSArray> receiver = ReceiverInputAs<JSArray>(); 1645 TNode<Object> fncallback = ArgumentOrUndefined(0); 1646 TNode<Object> this_arg = ArgumentOrUndefined(1); 1647 1648 TNode<Number> original_length = LoadJSArrayLength(receiver, kind); 1649 1650 // If the array length >= kMaxFastArrayLength, then CreateArray 1651 // will create a dictionary. We should deopt in this case, and make sure 1652 // not to attempt inlining again. 1653 original_length = CheckBounds(original_length, 1654 NumberConstant(JSArray::kMaxFastArrayLength)); 1655 1656 // Even though {JSCreateArray} is not marked as {kNoThrow}, we can elide the 1657 // exceptional projections because it cannot throw with the given 1658 // parameters. 1659 TNode<Object> array_ctor = 1660 Constant(native_context.GetInitialJSArrayMap(kind).GetConstructor()); 1661 1662 MapFrameStateParams frame_state_params{ 1663 jsgraph(), shared, context, target, outer_frame_state, 1664 receiver, fncallback, this_arg, {} /* TBD */, original_length}; 1665 1666 TNode<JSArray> a = 1667 CreateArrayNoThrow(array_ctor, original_length, 1668 MapPreLoopLazyFrameState(frame_state_params)); 1669 frame_state_params.a = a; 1670 1671 ThrowIfNotCallable(fncallback, 1672 MapLoopLazyFrameState(frame_state_params, ZeroConstant())); 1673 1674 ForZeroUntil(original_length).Do([&](TNode<Number> k) { 1675 Checkpoint(MapLoopEagerFrameState(frame_state_params, k)); 1676 MaybeInsertMapChecks(inference, has_stability_dependency); 1677 1678 TNode<Object> element; 1679 std::tie(k, element) = SafeLoadElement(kind, receiver, k); 1680 1681 auto continue_label = MakeLabel(); 1682 element = MaybeSkipHole(element, kind, &continue_label); 1683 1684 TNode<Object> v = JSCall3(fncallback, this_arg, element, k, receiver, 1685 MapLoopLazyFrameState(frame_state_params, k)); 1686 1687 // The array {a} should be HOLEY_SMI_ELEMENTS because we'd only come into 1688 // this loop if the input array length is non-zero, and "new Array({x > 0})" 1689 // always produces a HOLEY array. 1690 MapRef holey_double_map = 1691 native_context.GetInitialJSArrayMap(HOLEY_DOUBLE_ELEMENTS); 1692 MapRef holey_map = native_context.GetInitialJSArrayMap(HOLEY_ELEMENTS); 1693 TransitionAndStoreElement(holey_double_map, holey_map, a, k, v); 1694 1695 Goto(&continue_label); 1696 Bind(&continue_label); 1697 }); 1698 1699 return a; 1700} 1701 1702namespace { 1703 1704struct FilterFrameStateParams { 1705 JSGraph* jsgraph; 1706 SharedFunctionInfoRef shared; 1707 TNode<Context> context; 1708 TNode<Object> target; 1709 FrameState outer_frame_state; 1710 TNode<Object> receiver; 1711 TNode<Object> callback; 1712 TNode<Object> this_arg; 1713 TNode<JSArray> a; 1714 TNode<Object> original_length; 1715}; 1716 1717FrameState FilterLoopLazyFrameState(const FilterFrameStateParams& params, 1718 TNode<Number> k, TNode<Number> to, 1719 TNode<Object> element) { 1720 Node* checkpoint_params[] = {params.receiver, 1721 params.callback, 1722 params.this_arg, 1723 params.a, 1724 k, 1725 params.original_length, 1726 element, 1727 to}; 1728 return CreateJavaScriptBuiltinContinuationFrameState( 1729 params.jsgraph, params.shared, 1730 Builtin::kArrayFilterLoopLazyDeoptContinuation, params.target, 1731 params.context, checkpoint_params, arraysize(checkpoint_params), 1732 params.outer_frame_state, ContinuationFrameStateMode::LAZY); 1733} 1734 1735FrameState FilterLoopEagerPostCallbackFrameState( 1736 const FilterFrameStateParams& params, TNode<Number> k, TNode<Number> to, 1737 TNode<Object> element, TNode<Object> callback_value) { 1738 // Note that we are intentionally reusing the 1739 // Builtin::kArrayFilterLoopLazyDeoptContinuation as an *eager* entry 1740 // point in this case. This is safe, because re-evaluating a [ToBoolean] 1741 // coercion is safe. 1742 Node* checkpoint_params[] = {params.receiver, 1743 params.callback, 1744 params.this_arg, 1745 params.a, 1746 k, 1747 params.original_length, 1748 element, 1749 to, 1750 callback_value}; 1751 return CreateJavaScriptBuiltinContinuationFrameState( 1752 params.jsgraph, params.shared, 1753 Builtin::kArrayFilterLoopLazyDeoptContinuation, params.target, 1754 params.context, checkpoint_params, arraysize(checkpoint_params), 1755 params.outer_frame_state, ContinuationFrameStateMode::EAGER); 1756} 1757 1758FrameState FilterLoopEagerFrameState(const FilterFrameStateParams& params, 1759 TNode<Number> k, TNode<Number> to) { 1760 Node* checkpoint_params[] = {params.receiver, 1761 params.callback, 1762 params.this_arg, 1763 params.a, 1764 k, 1765 params.original_length, 1766 to}; 1767 return CreateJavaScriptBuiltinContinuationFrameState( 1768 params.jsgraph, params.shared, 1769 Builtin::kArrayFilterLoopEagerDeoptContinuation, params.target, 1770 params.context, checkpoint_params, arraysize(checkpoint_params), 1771 params.outer_frame_state, ContinuationFrameStateMode::EAGER); 1772} 1773 1774} // namespace 1775 1776TNode<JSArray> 1777IteratingArrayBuiltinReducerAssembler::ReduceArrayPrototypeFilter( 1778 MapInference* inference, const bool has_stability_dependency, 1779 ElementsKind kind, const SharedFunctionInfoRef& shared, 1780 const NativeContextRef& native_context) { 1781 FrameState outer_frame_state = FrameStateInput(); 1782 TNode<Context> context = ContextInput(); 1783 TNode<Object> target = TargetInput(); 1784 TNode<JSArray> receiver = ReceiverInputAs<JSArray>(); 1785 TNode<Object> fncallback = ArgumentOrUndefined(0); 1786 TNode<Object> this_arg = ArgumentOrUndefined(1); 1787 1788 // The output array is packed (filter doesn't visit holes). 1789 const ElementsKind packed_kind = GetPackedElementsKind(kind); 1790 TNode<JSArray> a = AllocateEmptyJSArray(packed_kind, native_context); 1791 1792 TNode<Number> original_length = LoadJSArrayLength(receiver, kind); 1793 1794 FilterFrameStateParams frame_state_params{ 1795 jsgraph(), shared, context, target, outer_frame_state, 1796 receiver, fncallback, this_arg, a, original_length}; 1797 1798 // This frame state doesn't ever call the deopt continuation, it's only 1799 // necessary to specify a continuation in order to handle the exceptional 1800 // case. We don't have all the values available to completely fill out 1801 // the checkpoint parameters yet, but that's okay because it'll never be 1802 // called. 1803 TNode<Number> zero = ZeroConstant(); 1804 ThrowIfNotCallable(fncallback, FilterLoopLazyFrameState(frame_state_params, 1805 zero, zero, zero)); 1806 1807 TNode<Number> initial_a_length = zero; 1808 For1ZeroUntil(original_length, initial_a_length) 1809 .Do([&](TNode<Number> k, TNode<Object>* a_length_object) { 1810 TNode<Number> a_length = TNode<Number>::UncheckedCast(*a_length_object); 1811 Checkpoint(FilterLoopEagerFrameState(frame_state_params, k, a_length)); 1812 MaybeInsertMapChecks(inference, has_stability_dependency); 1813 1814 TNode<Object> element; 1815 std::tie(k, element) = SafeLoadElement(kind, receiver, k); 1816 1817 auto continue_label = MakeLabel(MachineRepresentation::kTaggedSigned); 1818 element = MaybeSkipHole(element, kind, &continue_label, a_length); 1819 1820 TNode<Object> v = JSCall3( 1821 fncallback, this_arg, element, k, receiver, 1822 FilterLoopLazyFrameState(frame_state_params, k, a_length, element)); 1823 1824 // We need an eager frame state for right after the callback function 1825 // returned, just in case an attempt to grow the output array fails. 1826 Checkpoint(FilterLoopEagerPostCallbackFrameState(frame_state_params, k, 1827 a_length, element, v)); 1828 1829 GotoIfNot(ToBoolean(v), &continue_label, a_length); 1830 1831 // Since the callback returned a trueish value, store the element in a. 1832 { 1833 TNode<Number> a_length1 = TypeGuardFixedArrayLength(a_length); 1834 TNode<FixedArrayBase> elements = LoadElements(a); 1835 elements = MaybeGrowFastElements(kind, FeedbackSource{}, a, elements, 1836 a_length1, 1837 LoadFixedArrayBaseLength(elements)); 1838 1839 TNode<Number> new_a_length = NumberInc(a_length1); 1840 StoreJSArrayLength(a, new_a_length, kind); 1841 StoreFixedArrayBaseElement(elements, a_length1, element, kind); 1842 1843 Goto(&continue_label, new_a_length); 1844 } 1845 1846 Bind(&continue_label); 1847 *a_length_object = 1848 TNode<Object>::UncheckedCast(continue_label.PhiAt(0)); 1849 }) 1850 .ValueIsUnused(); 1851 1852 return a; 1853} 1854 1855namespace { 1856 1857struct FindFrameStateParams { 1858 JSGraph* jsgraph; 1859 SharedFunctionInfoRef shared; 1860 TNode<Context> context; 1861 TNode<Object> target; 1862 FrameState outer_frame_state; 1863 TNode<Object> receiver; 1864 TNode<Object> callback; 1865 TNode<Object> this_arg; 1866 TNode<Object> original_length; 1867}; 1868 1869FrameState FindLoopLazyFrameState(const FindFrameStateParams& params, 1870 TNode<Number> k, ArrayFindVariant variant) { 1871 Builtin builtin = (variant == ArrayFindVariant::kFind) 1872 ? Builtin::kArrayFindLoopLazyDeoptContinuation 1873 : Builtin::kArrayFindIndexLoopLazyDeoptContinuation; 1874 Node* checkpoint_params[] = {params.receiver, params.callback, 1875 params.this_arg, k, params.original_length}; 1876 return CreateJavaScriptBuiltinContinuationFrameState( 1877 params.jsgraph, params.shared, builtin, params.target, params.context, 1878 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1879 ContinuationFrameStateMode::LAZY); 1880} 1881 1882FrameState FindLoopEagerFrameState(const FindFrameStateParams& params, 1883 TNode<Number> k, ArrayFindVariant variant) { 1884 Builtin builtin = (variant == ArrayFindVariant::kFind) 1885 ? Builtin::kArrayFindLoopEagerDeoptContinuation 1886 : Builtin::kArrayFindIndexLoopEagerDeoptContinuation; 1887 Node* checkpoint_params[] = {params.receiver, params.callback, 1888 params.this_arg, k, params.original_length}; 1889 return CreateJavaScriptBuiltinContinuationFrameState( 1890 params.jsgraph, params.shared, builtin, params.target, params.context, 1891 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1892 ContinuationFrameStateMode::EAGER); 1893} 1894 1895FrameState FindLoopAfterCallbackLazyFrameState( 1896 const FindFrameStateParams& params, TNode<Number> next_k, 1897 TNode<Object> if_found_value, ArrayFindVariant variant) { 1898 Builtin builtin = 1899 (variant == ArrayFindVariant::kFind) 1900 ? Builtin::kArrayFindLoopAfterCallbackLazyDeoptContinuation 1901 : Builtin::kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation; 1902 Node* checkpoint_params[] = {params.receiver, params.callback, 1903 params.this_arg, next_k, 1904 params.original_length, if_found_value}; 1905 return CreateJavaScriptBuiltinContinuationFrameState( 1906 params.jsgraph, params.shared, builtin, params.target, params.context, 1907 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1908 ContinuationFrameStateMode::LAZY); 1909} 1910 1911} // namespace 1912 1913TNode<Object> IteratingArrayBuiltinReducerAssembler::ReduceArrayPrototypeFind( 1914 MapInference* inference, const bool has_stability_dependency, 1915 ElementsKind kind, const SharedFunctionInfoRef& shared, 1916 const NativeContextRef& native_context, ArrayFindVariant variant) { 1917 FrameState outer_frame_state = FrameStateInput(); 1918 TNode<Context> context = ContextInput(); 1919 TNode<Object> target = TargetInput(); 1920 TNode<JSArray> receiver = ReceiverInputAs<JSArray>(); 1921 TNode<Object> fncallback = ArgumentOrUndefined(0); 1922 TNode<Object> this_arg = ArgumentOrUndefined(1); 1923 1924 TNode<Number> original_length = LoadJSArrayLength(receiver, kind); 1925 1926 FindFrameStateParams frame_state_params{ 1927 jsgraph(), shared, context, target, outer_frame_state, 1928 receiver, fncallback, this_arg, original_length}; 1929 1930 ThrowIfNotCallable( 1931 fncallback, 1932 FindLoopLazyFrameState(frame_state_params, ZeroConstant(), variant)); 1933 1934 const bool is_find_variant = (variant == ArrayFindVariant::kFind); 1935 auto out = MakeLabel(MachineRepresentation::kTagged); 1936 1937 ForZeroUntil(original_length).Do([&](TNode<Number> k) { 1938 Checkpoint(FindLoopEagerFrameState(frame_state_params, k, variant)); 1939 MaybeInsertMapChecks(inference, has_stability_dependency); 1940 1941 TNode<Object> element; 1942 std::tie(k, element) = SafeLoadElement(kind, receiver, k); 1943 1944 if (IsHoleyElementsKind(kind)) { 1945 element = TryConvertHoleToUndefined(element, kind); 1946 } 1947 1948 TNode<Object> if_found_value = is_find_variant ? element : k; 1949 TNode<Number> next_k = NumberInc(k); 1950 1951 // The callback result states whether the desired element was found. 1952 TNode<Object> v = 1953 JSCall3(fncallback, this_arg, element, k, receiver, 1954 FindLoopAfterCallbackLazyFrameState(frame_state_params, next_k, 1955 if_found_value, variant)); 1956 1957 GotoIf(ToBoolean(v), &out, if_found_value); 1958 }); 1959 1960 // If the loop completed, the element was not found. 1961 TNode<Object> if_not_found_value = 1962 is_find_variant ? TNode<Object>::UncheckedCast(UndefinedConstant()) 1963 : TNode<Object>::UncheckedCast(MinusOneConstant()); 1964 Goto(&out, if_not_found_value); 1965 1966 Bind(&out); 1967 return out.PhiAt<Object>(0); 1968} 1969 1970namespace { 1971 1972struct EverySomeFrameStateParams { 1973 JSGraph* jsgraph; 1974 SharedFunctionInfoRef shared; 1975 TNode<Context> context; 1976 TNode<Object> target; 1977 FrameState outer_frame_state; 1978 TNode<Object> receiver; 1979 TNode<Object> callback; 1980 TNode<Object> this_arg; 1981 TNode<Object> original_length; 1982}; 1983 1984FrameState EverySomeLoopLazyFrameState(const EverySomeFrameStateParams& params, 1985 TNode<Number> k, 1986 ArrayEverySomeVariant variant) { 1987 Builtin builtin = (variant == ArrayEverySomeVariant::kEvery) 1988 ? Builtin::kArrayEveryLoopLazyDeoptContinuation 1989 : Builtin::kArraySomeLoopLazyDeoptContinuation; 1990 Node* checkpoint_params[] = {params.receiver, params.callback, 1991 params.this_arg, k, params.original_length}; 1992 return CreateJavaScriptBuiltinContinuationFrameState( 1993 params.jsgraph, params.shared, builtin, params.target, params.context, 1994 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 1995 ContinuationFrameStateMode::LAZY); 1996} 1997 1998FrameState EverySomeLoopEagerFrameState(const EverySomeFrameStateParams& params, 1999 TNode<Number> k, 2000 ArrayEverySomeVariant variant) { 2001 Builtin builtin = (variant == ArrayEverySomeVariant::kEvery) 2002 ? Builtin::kArrayEveryLoopEagerDeoptContinuation 2003 : Builtin::kArraySomeLoopEagerDeoptContinuation; 2004 Node* checkpoint_params[] = {params.receiver, params.callback, 2005 params.this_arg, k, params.original_length}; 2006 return CreateJavaScriptBuiltinContinuationFrameState( 2007 params.jsgraph, params.shared, builtin, params.target, params.context, 2008 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state, 2009 ContinuationFrameStateMode::EAGER); 2010} 2011 2012} // namespace 2013 2014TNode<Boolean> 2015IteratingArrayBuiltinReducerAssembler::ReduceArrayPrototypeEverySome( 2016 MapInference* inference, const bool has_stability_dependency, 2017 ElementsKind kind, const SharedFunctionInfoRef& shared, 2018 const NativeContextRef& native_context, ArrayEverySomeVariant variant) { 2019 FrameState outer_frame_state = FrameStateInput(); 2020 TNode<Context> context = ContextInput(); 2021 TNode<Object> target = TargetInput(); 2022 TNode<JSArray> receiver = ReceiverInputAs<JSArray>(); 2023 TNode<Object> fncallback = ArgumentOrUndefined(0); 2024 TNode<Object> this_arg = ArgumentOrUndefined(1); 2025 2026 TNode<Number> original_length = LoadJSArrayLength(receiver, kind); 2027 2028 EverySomeFrameStateParams frame_state_params{ 2029 jsgraph(), shared, context, target, outer_frame_state, 2030 receiver, fncallback, this_arg, original_length}; 2031 2032 ThrowIfNotCallable( 2033 fncallback, 2034 EverySomeLoopLazyFrameState(frame_state_params, ZeroConstant(), variant)); 2035 2036 auto out = MakeLabel(MachineRepresentation::kTagged); 2037 2038 ForZeroUntil(original_length).Do([&](TNode<Number> k) { 2039 Checkpoint(EverySomeLoopEagerFrameState(frame_state_params, k, variant)); 2040 MaybeInsertMapChecks(inference, has_stability_dependency); 2041 2042 TNode<Object> element; 2043 std::tie(k, element) = SafeLoadElement(kind, receiver, k); 2044 2045 auto continue_label = MakeLabel(); 2046 element = MaybeSkipHole(element, kind, &continue_label); 2047 2048 TNode<Object> v = 2049 JSCall3(fncallback, this_arg, element, k, receiver, 2050 EverySomeLoopLazyFrameState(frame_state_params, k, variant)); 2051 2052 if (variant == ArrayEverySomeVariant::kEvery) { 2053 GotoIfNot(ToBoolean(v), &out, FalseConstant()); 2054 } else { 2055 DCHECK_EQ(variant, ArrayEverySomeVariant::kSome); 2056 GotoIf(ToBoolean(v), &out, TrueConstant()); 2057 } 2058 Goto(&continue_label); 2059 Bind(&continue_label); 2060 }); 2061 2062 Goto(&out, (variant == ArrayEverySomeVariant::kEvery) ? TrueConstant() 2063 : FalseConstant()); 2064 2065 Bind(&out); 2066 return out.PhiAt<Boolean>(0); 2067} 2068 2069namespace { 2070 2071Callable GetCallableForArrayIndexOfIncludes(ArrayIndexOfIncludesVariant variant, 2072 ElementsKind elements_kind, 2073 Isolate* isolate) { 2074 if (variant == ArrayIndexOfIncludesVariant::kIndexOf) { 2075 switch (elements_kind) { 2076 case PACKED_SMI_ELEMENTS: 2077 case HOLEY_SMI_ELEMENTS: 2078 case PACKED_ELEMENTS: 2079 case HOLEY_ELEMENTS: 2080 return Builtins::CallableFor(isolate, 2081 Builtin::kArrayIndexOfSmiOrObject); 2082 case PACKED_DOUBLE_ELEMENTS: 2083 return Builtins::CallableFor(isolate, 2084 Builtin::kArrayIndexOfPackedDoubles); 2085 default: 2086 DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind); 2087 return Builtins::CallableFor(isolate, 2088 Builtin::kArrayIndexOfHoleyDoubles); 2089 } 2090 } else { 2091 DCHECK_EQ(variant, ArrayIndexOfIncludesVariant::kIncludes); 2092 switch (elements_kind) { 2093 case PACKED_SMI_ELEMENTS: 2094 case HOLEY_SMI_ELEMENTS: 2095 case PACKED_ELEMENTS: 2096 case HOLEY_ELEMENTS: 2097 return Builtins::CallableFor(isolate, 2098 Builtin::kArrayIncludesSmiOrObject); 2099 case PACKED_DOUBLE_ELEMENTS: 2100 return Builtins::CallableFor(isolate, 2101 Builtin::kArrayIncludesPackedDoubles); 2102 default: 2103 DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind); 2104 return Builtins::CallableFor(isolate, 2105 Builtin::kArrayIncludesHoleyDoubles); 2106 } 2107 } 2108 UNREACHABLE(); 2109} 2110 2111} // namespace 2112 2113TNode<Object> 2114IteratingArrayBuiltinReducerAssembler::ReduceArrayPrototypeIndexOfIncludes( 2115 ElementsKind kind, ArrayIndexOfIncludesVariant variant) { 2116 TNode<Context> context = ContextInput(); 2117 TNode<JSArray> receiver = ReceiverInputAs<JSArray>(); 2118 TNode<Object> search_element = ArgumentOrUndefined(0); 2119 TNode<Object> from_index = ArgumentOrZero(1); 2120 2121 // TODO(jgruber): This currently only reduces to a stub call. Create a full 2122 // reduction (similar to other higher-order array builtins) instead of 2123 // lowering to a builtin call. E.g. Array.p.every and Array.p.some have almost 2124 // identical functionality. 2125 2126 TNode<Number> length = LoadJSArrayLength(receiver, kind); 2127 TNode<FixedArrayBase> elements = LoadElements(receiver); 2128 2129 const bool have_from_index = ArgumentCount() > 1; 2130 if (have_from_index) { 2131 TNode<Smi> from_index_smi = CheckSmi(from_index); 2132 2133 // If the index is negative, it means the offset from the end and 2134 // therefore needs to be added to the length. If the result is still 2135 // negative, it needs to be clamped to 0. 2136 TNode<Boolean> cond = NumberLessThan(from_index_smi, ZeroConstant()); 2137 from_index = SelectIf<Number>(cond) 2138 .Then(_ { 2139 return NumberMax(NumberAdd(length, from_index_smi), 2140 ZeroConstant()); 2141 }) 2142 .Else(_ { return from_index_smi; }) 2143 .ExpectFalse() 2144 .Value(); 2145 } 2146 2147 return Call4(GetCallableForArrayIndexOfIncludes(variant, kind, isolate()), 2148 context, elements, search_element, length, from_index); 2149} 2150 2151namespace { 2152 2153struct PromiseCtorFrameStateParams { 2154 JSGraph* jsgraph; 2155 SharedFunctionInfoRef shared; 2156 Node* node_ptr; 2157 TNode<Context> context; 2158 TNode<Object> target; 2159 FrameState outer_frame_state; 2160}; 2161 2162// Remnant of old-style JSCallReducer code. Could be ported to graph assembler, 2163// but probably not worth the effort. 2164FrameState CreateArtificialFrameState( 2165 Node* node, Node* outer_frame_state, int parameter_count, 2166 BytecodeOffset bailout_id, FrameStateType frame_state_type, 2167 const SharedFunctionInfoRef& shared, Node* context, 2168 CommonOperatorBuilder* common, Graph* graph) { 2169 const FrameStateFunctionInfo* state_info = 2170 common->CreateFrameStateFunctionInfo( 2171 frame_state_type, parameter_count + 1, 0, shared.object()); 2172 2173 const Operator* op = common->FrameState( 2174 bailout_id, OutputFrameStateCombine::Ignore(), state_info); 2175 const Operator* op0 = common->StateValues(0, SparseInputMask::Dense()); 2176 Node* node0 = graph->NewNode(op0); 2177 2178 static constexpr int kTargetInputIndex = 0; 2179 static constexpr int kReceiverInputIndex = 1; 2180 const int parameter_count_with_receiver = parameter_count + 1; 2181 std::vector<Node*> params; 2182 params.reserve(parameter_count_with_receiver); 2183 for (int i = 0; i < parameter_count_with_receiver; i++) { 2184 params.push_back(node->InputAt(kReceiverInputIndex + i)); 2185 } 2186 const Operator* op_param = common->StateValues( 2187 static_cast<int>(params.size()), SparseInputMask::Dense()); 2188 Node* params_node = graph->NewNode(op_param, static_cast<int>(params.size()), 2189 ¶ms.front()); 2190 DCHECK(context); 2191 return FrameState(graph->NewNode(op, params_node, node0, node0, context, 2192 node->InputAt(kTargetInputIndex), 2193 outer_frame_state)); 2194} 2195 2196FrameState PromiseConstructorFrameState( 2197 const PromiseCtorFrameStateParams& params, CommonOperatorBuilder* common, 2198 Graph* graph) { 2199 DCHECK_EQ(1, 2200 params.shared.internal_formal_parameter_count_without_receiver()); 2201 return CreateArtificialFrameState( 2202 params.node_ptr, params.outer_frame_state, 1, 2203 BytecodeOffset::ConstructStubInvoke(), FrameStateType::kConstructStub, 2204 params.shared, params.context, common, graph); 2205} 2206 2207FrameState PromiseConstructorLazyFrameState( 2208 const PromiseCtorFrameStateParams& params, 2209 FrameState constructor_frame_state) { 2210 // The deopt continuation of this frame state is never called; the frame state 2211 // is only necessary to obtain the right stack trace. 2212 JSGraph* jsgraph = params.jsgraph; 2213 Node* checkpoint_params[] = { 2214 jsgraph->UndefinedConstant(), /* receiver */ 2215 jsgraph->UndefinedConstant(), /* promise */ 2216 jsgraph->UndefinedConstant(), /* reject function */ 2217 jsgraph->TheHoleConstant() /* exception */ 2218 }; 2219 return CreateJavaScriptBuiltinContinuationFrameState( 2220 jsgraph, params.shared, Builtin::kPromiseConstructorLazyDeoptContinuation, 2221 params.target, params.context, checkpoint_params, 2222 arraysize(checkpoint_params), constructor_frame_state, 2223 ContinuationFrameStateMode::LAZY); 2224} 2225 2226FrameState PromiseConstructorLazyWithCatchFrameState( 2227 const PromiseCtorFrameStateParams& params, 2228 FrameState constructor_frame_state, TNode<JSPromise> promise, 2229 TNode<JSFunction> reject) { 2230 // This continuation just returns the created promise and takes care of 2231 // exceptions thrown by the executor. 2232 Node* checkpoint_params[] = { 2233 params.jsgraph->UndefinedConstant(), /* receiver */ 2234 promise, reject}; 2235 return CreateJavaScriptBuiltinContinuationFrameState( 2236 params.jsgraph, params.shared, 2237 Builtin::kPromiseConstructorLazyDeoptContinuation, params.target, 2238 params.context, checkpoint_params, arraysize(checkpoint_params), 2239 constructor_frame_state, ContinuationFrameStateMode::LAZY_WITH_CATCH); 2240} 2241 2242} // namespace 2243 2244TNode<Object> PromiseBuiltinReducerAssembler::ReducePromiseConstructor( 2245 const NativeContextRef& native_context) { 2246 DCHECK_GE(ConstructArity(), 1); 2247 2248 JSConstructNode n(node_ptr()); 2249 FrameState outer_frame_state = FrameStateInput(); 2250 TNode<Context> context = ContextInput(); 2251 TNode<Object> target = TargetInput(); 2252 TNode<Object> executor = n.Argument(0); 2253 DCHECK_EQ(target, NewTargetInput()); 2254 2255 SharedFunctionInfoRef promise_shared = 2256 native_context.promise_function().shared(); 2257 2258 PromiseCtorFrameStateParams frame_state_params{jsgraph(), promise_shared, 2259 node_ptr(), context, 2260 target, outer_frame_state}; 2261 2262 // Insert a construct stub frame into the chain of frame states. This will 2263 // reconstruct the proper frame when deoptimizing within the constructor. 2264 // For the frame state, we only provide the executor parameter, even if more 2265 // arguments were passed. This is not observable from JS. 2266 FrameState constructor_frame_state = 2267 PromiseConstructorFrameState(frame_state_params, common(), graph()); 2268 2269 ThrowIfNotCallable(executor, 2270 PromiseConstructorLazyFrameState(frame_state_params, 2271 constructor_frame_state)); 2272 2273 TNode<JSPromise> promise = CreatePromise(context); 2274 2275 // 8. CreatePromiseResolvingFunctions 2276 // Allocate a promise context for the closures below. 2277 TNode<Context> promise_context = CreateFunctionContext( 2278 native_context, context, PromiseBuiltins::kPromiseContextLength); 2279 StoreContextSlot(promise_context, PromiseBuiltins::kPromiseSlot, promise); 2280 StoreContextSlot(promise_context, PromiseBuiltins::kAlreadyResolvedSlot, 2281 FalseConstant()); 2282 StoreContextSlot(promise_context, PromiseBuiltins::kDebugEventSlot, 2283 TrueConstant()); 2284 2285 // Allocate closures for the resolve and reject cases. 2286 SharedFunctionInfoRef resolve_sfi = 2287 MakeRef(broker_, broker_->isolate() 2288 ->factory() 2289 ->promise_capability_default_resolve_shared_fun()); 2290 TNode<JSFunction> resolve = 2291 CreateClosureFromBuiltinSharedFunctionInfo(resolve_sfi, promise_context); 2292 2293 SharedFunctionInfoRef reject_sfi = 2294 MakeRef(broker_, broker_->isolate() 2295 ->factory() 2296 ->promise_capability_default_reject_shared_fun()); 2297 TNode<JSFunction> reject = 2298 CreateClosureFromBuiltinSharedFunctionInfo(reject_sfi, promise_context); 2299 2300 FrameState lazy_with_catch_frame_state = 2301 PromiseConstructorLazyWithCatchFrameState( 2302 frame_state_params, constructor_frame_state, promise, reject); 2303 2304 // 9. Call executor with both resolving functions. 2305 // 10a. Call reject if the call to executor threw. 2306 Try(_ { 2307 CallPromiseExecutor(executor, resolve, reject, lazy_with_catch_frame_state); 2308 }).Catch([&](TNode<Object> exception) { 2309 CallPromiseReject(reject, exception, lazy_with_catch_frame_state); 2310 }); 2311 2312 return promise; 2313} 2314 2315#undef _ 2316 2317Reduction JSCallReducer::ReplaceWithSubgraph(JSCallReducerAssembler* gasm, 2318 Node* subgraph) { 2319 // TODO(jgruber): Consider a less fiddly way of integrating the new subgraph 2320 // into the outer graph. For instance, the subgraph could be created in 2321 // complete isolation, and then plugged into the outer graph in one go. 2322 // Instead of manually tracking IfException nodes, we could iterate the 2323 // subgraph. 2324 2325 // Replace the Call node with the newly-produced subgraph. 2326 ReplaceWithValue(gasm->node_ptr(), subgraph, gasm->effect(), gasm->control()); 2327 2328 // Wire exception edges contained in the newly-produced subgraph into the 2329 // outer graph. 2330 auto catch_scope = gasm->catch_scope(); 2331 DCHECK(catch_scope->is_outermost()); 2332 2333 if (catch_scope->has_handler() && 2334 catch_scope->has_exceptional_control_flow()) { 2335 TNode<Object> handler_exception; 2336 Effect handler_effect{nullptr}; 2337 Control handler_control{nullptr}; 2338 gasm->catch_scope()->MergeExceptionalPaths( 2339 &handler_exception, &handler_effect, &handler_control); 2340 2341 ReplaceWithValue(gasm->outermost_handler(), handler_exception, 2342 handler_effect, handler_control); 2343 } 2344 2345 return Replace(subgraph); 2346} 2347 2348Reduction JSCallReducer::ReduceMathUnary(Node* node, const Operator* op) { 2349 JSCallNode n(node); 2350 CallParameters const& p = n.Parameters(); 2351 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 2352 return NoChange(); 2353 } 2354 if (n.ArgumentCount() < 1) { 2355 Node* value = jsgraph()->NaNConstant(); 2356 ReplaceWithValue(node, value); 2357 return Replace(value); 2358 } 2359 2360 JSCallReducerAssembler a(this, node); 2361 Node* subgraph = a.ReduceMathUnary(op); 2362 return ReplaceWithSubgraph(&a, subgraph); 2363} 2364 2365Reduction JSCallReducer::ReduceMathBinary(Node* node, const Operator* op) { 2366 JSCallNode n(node); 2367 CallParameters const& p = n.Parameters(); 2368 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 2369 return NoChange(); 2370 } 2371 if (n.ArgumentCount() < 1) { 2372 Node* value = jsgraph()->NaNConstant(); 2373 ReplaceWithValue(node, value); 2374 return Replace(value); 2375 } 2376 2377 JSCallReducerAssembler a(this, node); 2378 Node* subgraph = a.ReduceMathBinary(op); 2379 return ReplaceWithSubgraph(&a, subgraph); 2380} 2381 2382// ES6 section 20.2.2.19 Math.imul ( x, y ) 2383Reduction JSCallReducer::ReduceMathImul(Node* node) { 2384 JSCallNode n(node); 2385 CallParameters const& p = n.Parameters(); 2386 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 2387 return NoChange(); 2388 } 2389 if (n.ArgumentCount() < 1) { 2390 Node* value = jsgraph()->ZeroConstant(); 2391 ReplaceWithValue(node, value); 2392 return Replace(value); 2393 } 2394 Node* left = n.Argument(0); 2395 Node* right = n.ArgumentOr(1, jsgraph()->ZeroConstant()); 2396 Effect effect = n.effect(); 2397 Control control = n.control(); 2398 2399 left = effect = 2400 graph()->NewNode(simplified()->SpeculativeToNumber( 2401 NumberOperationHint::kNumberOrOddball, p.feedback()), 2402 left, effect, control); 2403 right = effect = 2404 graph()->NewNode(simplified()->SpeculativeToNumber( 2405 NumberOperationHint::kNumberOrOddball, p.feedback()), 2406 right, effect, control); 2407 left = graph()->NewNode(simplified()->NumberToUint32(), left); 2408 right = graph()->NewNode(simplified()->NumberToUint32(), right); 2409 Node* value = graph()->NewNode(simplified()->NumberImul(), left, right); 2410 ReplaceWithValue(node, value, effect); 2411 return Replace(value); 2412} 2413 2414// ES6 section 20.2.2.11 Math.clz32 ( x ) 2415Reduction JSCallReducer::ReduceMathClz32(Node* node) { 2416 JSCallNode n(node); 2417 CallParameters const& p = n.Parameters(); 2418 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 2419 return NoChange(); 2420 } 2421 if (n.ArgumentCount() < 1) { 2422 Node* value = jsgraph()->Constant(32); 2423 ReplaceWithValue(node, value); 2424 return Replace(value); 2425 } 2426 Node* input = n.Argument(0); 2427 Effect effect = n.effect(); 2428 Control control = n.control(); 2429 2430 input = effect = 2431 graph()->NewNode(simplified()->SpeculativeToNumber( 2432 NumberOperationHint::kNumberOrOddball, p.feedback()), 2433 input, effect, control); 2434 input = graph()->NewNode(simplified()->NumberToUint32(), input); 2435 Node* value = graph()->NewNode(simplified()->NumberClz32(), input); 2436 ReplaceWithValue(node, value, effect); 2437 return Replace(value); 2438} 2439 2440// ES6 section 20.2.2.24 Math.max ( value1, value2, ...values ) 2441// ES6 section 20.2.2.25 Math.min ( value1, value2, ...values ) 2442Reduction JSCallReducer::ReduceMathMinMax(Node* node, const Operator* op, 2443 Node* empty_value) { 2444 JSCallNode n(node); 2445 CallParameters const& p = n.Parameters(); 2446 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 2447 return NoChange(); 2448 } 2449 if (n.ArgumentCount() < 1) { 2450 ReplaceWithValue(node, empty_value); 2451 return Replace(empty_value); 2452 } 2453 Node* effect = NodeProperties::GetEffectInput(node); 2454 Node* control = NodeProperties::GetControlInput(node); 2455 2456 Node* value = effect = 2457 graph()->NewNode(simplified()->SpeculativeToNumber( 2458 NumberOperationHint::kNumberOrOddball, p.feedback()), 2459 n.Argument(0), effect, control); 2460 for (int i = 1; i < n.ArgumentCount(); i++) { 2461 Node* input = effect = graph()->NewNode( 2462 simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball, 2463 p.feedback()), 2464 n.Argument(i), effect, control); 2465 value = graph()->NewNode(op, value, input); 2466 } 2467 2468 ReplaceWithValue(node, value, effect); 2469 return Replace(value); 2470} 2471 2472Reduction JSCallReducer::Reduce(Node* node) { 2473 switch (node->opcode()) { 2474 case IrOpcode::kJSConstruct: 2475 return ReduceJSConstruct(node); 2476 case IrOpcode::kJSConstructWithArrayLike: 2477 return ReduceJSConstructWithArrayLike(node); 2478 case IrOpcode::kJSConstructWithSpread: 2479 return ReduceJSConstructWithSpread(node); 2480 case IrOpcode::kJSCall: 2481 return ReduceJSCall(node); 2482 case IrOpcode::kJSCallWithArrayLike: 2483 return ReduceJSCallWithArrayLike(node); 2484 case IrOpcode::kJSCallWithSpread: 2485 return ReduceJSCallWithSpread(node); 2486 default: 2487 break; 2488 } 2489 return NoChange(); 2490} 2491 2492void JSCallReducer::Finalize() { 2493 // TODO(turbofan): This is not the best solution; ideally we would be able 2494 // to teach the GraphReducer about arbitrary dependencies between different 2495 // nodes, even if they don't show up in the use list of the other node. 2496 std::set<Node*> const waitlist = std::move(waitlist_); 2497 for (Node* node : waitlist) { 2498 if (!node->IsDead()) { 2499 Reduction const reduction = Reduce(node); 2500 if (reduction.Changed()) { 2501 Node* replacement = reduction.replacement(); 2502 if (replacement != node) { 2503 Replace(node, replacement); 2504 } 2505 } 2506 } 2507 } 2508} 2509 2510// ES6 section 22.1.1 The Array Constructor 2511Reduction JSCallReducer::ReduceArrayConstructor(Node* node) { 2512 JSCallNode n(node); 2513 Node* target = n.target(); 2514 CallParameters const& p = n.Parameters(); 2515 2516 // Turn the {node} into a {JSCreateArray} call. 2517 size_t const arity = p.arity_without_implicit_args(); 2518 node->RemoveInput(n.FeedbackVectorIndex()); 2519 NodeProperties::ReplaceValueInput(node, target, 0); 2520 NodeProperties::ReplaceValueInput(node, target, 1); 2521 NodeProperties::ChangeOp(node, 2522 javascript()->CreateArray(arity, base::nullopt)); 2523 return Changed(node); 2524} 2525 2526// ES6 section 19.3.1.1 Boolean ( value ) 2527Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) { 2528 // Replace the {node} with a proper {ToBoolean} operator. 2529 JSCallNode n(node); 2530 Node* value = n.ArgumentOrUndefined(0, jsgraph()); 2531 value = graph()->NewNode(simplified()->ToBoolean(), value); 2532 ReplaceWithValue(node, value); 2533 return Replace(value); 2534} 2535 2536// ES section #sec-object-constructor 2537Reduction JSCallReducer::ReduceObjectConstructor(Node* node) { 2538 JSCallNode n(node); 2539 if (n.ArgumentCount() < 1) return NoChange(); 2540 Node* value = n.Argument(0); 2541 Effect effect = n.effect(); 2542 2543 // We can fold away the Object(x) call if |x| is definitely not a primitive. 2544 if (NodeProperties::CanBePrimitive(broker(), value, effect)) { 2545 if (!NodeProperties::CanBeNullOrUndefined(broker(), value, effect)) { 2546 // Turn the {node} into a {JSToObject} call if we know that 2547 // the {value} cannot be null or undefined. 2548 NodeProperties::ReplaceValueInputs(node, value); 2549 NodeProperties::ChangeOp(node, javascript()->ToObject()); 2550 return Changed(node); 2551 } 2552 } else { 2553 ReplaceWithValue(node, value); 2554 return Replace(value); 2555 } 2556 return NoChange(); 2557} 2558 2559// ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) 2560Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { 2561 JSCallNode n(node); 2562 CallParameters const& p = n.Parameters(); 2563 CallFeedbackRelation new_feedback_relation = 2564 p.feedback_relation() == CallFeedbackRelation::kReceiver 2565 ? CallFeedbackRelation::kTarget 2566 : CallFeedbackRelation::kUnrelated; 2567 int arity = p.arity_without_implicit_args(); 2568 2569 if (arity < 2) { 2570 // Degenerate cases. 2571 ConvertReceiverMode convert_mode; 2572 if (arity == 0) { 2573 // Neither thisArg nor argArray was provided. 2574 convert_mode = ConvertReceiverMode::kNullOrUndefined; 2575 node->ReplaceInput(n.TargetIndex(), n.receiver()); 2576 node->ReplaceInput(n.ReceiverIndex(), jsgraph()->UndefinedConstant()); 2577 } else { 2578 DCHECK_EQ(arity, 1); 2579 // The argArray was not provided, just remove the {target}. 2580 convert_mode = ConvertReceiverMode::kAny; 2581 node->RemoveInput(n.TargetIndex()); 2582 --arity; 2583 } 2584 // Change {node} to a {JSCall} and try to reduce further. 2585 NodeProperties::ChangeOp( 2586 node, javascript()->Call(JSCallNode::ArityForArgc(arity), p.frequency(), 2587 p.feedback(), convert_mode, 2588 p.speculation_mode(), new_feedback_relation)); 2589 return Changed(node).FollowedBy(ReduceJSCall(node)); 2590 } 2591 2592 // Turn the JSCall into a JSCallWithArrayLike. 2593 // If {argArray} can be null or undefined, we have to generate branches since 2594 // JSCallWithArrayLike would throw for null or undefined. 2595 2596 Node* target = n.receiver(); 2597 Node* this_argument = n.Argument(0); 2598 Node* arguments_list = n.Argument(1); 2599 Node* context = n.context(); 2600 FrameState frame_state = n.frame_state(); 2601 Effect effect = n.effect(); 2602 Control control = n.control(); 2603 2604 // If {arguments_list} cannot be null or undefined, we don't need 2605 // to expand this {node} to control-flow. 2606 if (!NodeProperties::CanBeNullOrUndefined(broker(), arguments_list, effect)) { 2607 // Massage the value inputs appropriately. 2608 node->ReplaceInput(n.TargetIndex(), target); 2609 node->ReplaceInput(n.ReceiverIndex(), this_argument); 2610 node->ReplaceInput(n.ArgumentIndex(0), arguments_list); 2611 while (arity-- > 1) node->RemoveInput(n.ArgumentIndex(1)); 2612 2613 // Morph the {node} to a {JSCallWithArrayLike}. 2614 NodeProperties::ChangeOp( 2615 node, javascript()->CallWithArrayLike(p.frequency(), p.feedback(), 2616 p.speculation_mode(), 2617 new_feedback_relation)); 2618 return Changed(node).FollowedBy(ReduceJSCallWithArrayLike(node)); 2619 } 2620 2621 // Check whether {arguments_list} is null. 2622 Node* check_null = 2623 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list, 2624 jsgraph()->NullConstant()); 2625 control = graph()->NewNode(common()->Branch(BranchHint::kFalse), check_null, 2626 control); 2627 Node* if_null = graph()->NewNode(common()->IfTrue(), control); 2628 control = graph()->NewNode(common()->IfFalse(), control); 2629 2630 // Check whether {arguments_list} is undefined. 2631 Node* check_undefined = 2632 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list, 2633 jsgraph()->UndefinedConstant()); 2634 control = graph()->NewNode(common()->Branch(BranchHint::kFalse), 2635 check_undefined, control); 2636 Node* if_undefined = graph()->NewNode(common()->IfTrue(), control); 2637 control = graph()->NewNode(common()->IfFalse(), control); 2638 2639 // Lower to {JSCallWithArrayLike} if {arguments_list} is neither null 2640 // nor undefined. 2641 Node* effect0 = effect; 2642 Node* control0 = control; 2643 Node* value0 = effect0 = control0 = graph()->NewNode( 2644 javascript()->CallWithArrayLike(p.frequency(), p.feedback(), 2645 p.speculation_mode(), 2646 new_feedback_relation), 2647 target, this_argument, arguments_list, n.feedback_vector(), context, 2648 frame_state, effect0, control0); 2649 2650 // Lower to {JSCall} if {arguments_list} is either null or undefined. 2651 Node* effect1 = effect; 2652 Node* control1 = graph()->NewNode(common()->Merge(2), if_null, if_undefined); 2653 Node* value1 = effect1 = control1 = graph()->NewNode( 2654 javascript()->Call(JSCallNode::ArityForArgc(0)), target, this_argument, 2655 n.feedback_vector(), context, frame_state, effect1, control1); 2656 2657 // Rewire potential exception edges. 2658 Node* if_exception = nullptr; 2659 if (NodeProperties::IsExceptionalCall(node, &if_exception)) { 2660 // Create appropriate {IfException} and {IfSuccess} nodes. 2661 Node* if_exception0 = 2662 graph()->NewNode(common()->IfException(), control0, effect0); 2663 control0 = graph()->NewNode(common()->IfSuccess(), control0); 2664 Node* if_exception1 = 2665 graph()->NewNode(common()->IfException(), control1, effect1); 2666 control1 = graph()->NewNode(common()->IfSuccess(), control1); 2667 2668 // Join the exception edges. 2669 Node* merge = 2670 graph()->NewNode(common()->Merge(2), if_exception0, if_exception1); 2671 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0, 2672 if_exception1, merge); 2673 Node* phi = 2674 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 2675 if_exception0, if_exception1, merge); 2676 ReplaceWithValue(if_exception, phi, ephi, merge); 2677 } 2678 2679 // Join control paths. 2680 control = graph()->NewNode(common()->Merge(2), control0, control1); 2681 effect = graph()->NewNode(common()->EffectPhi(2), effect0, effect1, control); 2682 Node* value = 2683 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), value0, 2684 value1, control); 2685 ReplaceWithValue(node, value, effect, control); 2686 return Replace(value); 2687} 2688 2689// ES section #sec-function.prototype.bind 2690Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) { 2691 JSCallNode n(node); 2692 CallParameters const& p = n.Parameters(); 2693 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 2694 return NoChange(); 2695 } 2696 2697 // Value inputs to the {node} are as follows: 2698 // 2699 // - target, which is Function.prototype.bind JSFunction 2700 // - receiver, which is the [[BoundTargetFunction]] 2701 // - bound_this (optional), which is the [[BoundThis]] 2702 // - and all the remaining value inputs are [[BoundArguments]] 2703 Node* receiver = n.receiver(); 2704 Node* context = n.context(); 2705 Effect effect = n.effect(); 2706 Control control = n.control(); 2707 2708 // Ensure that the {receiver} is known to be a JSBoundFunction or 2709 // a JSFunction with the same [[Prototype]], and all maps we've 2710 // seen for the {receiver} so far indicate that {receiver} is 2711 // definitely a constructor or not a constructor. 2712 MapInference inference(broker(), receiver, effect); 2713 if (!inference.HaveMaps()) return NoChange(); 2714 ZoneVector<MapRef> const& receiver_maps = inference.GetMaps(); 2715 2716 MapRef first_receiver_map = receiver_maps[0]; 2717 bool const is_constructor = first_receiver_map.is_constructor(); 2718 2719 HeapObjectRef prototype = first_receiver_map.prototype(); 2720 2721 for (const MapRef& receiver_map : receiver_maps) { 2722 HeapObjectRef map_prototype = receiver_map.prototype(); 2723 2724 // Check for consistency among the {receiver_maps}. 2725 if (!map_prototype.equals(prototype) || 2726 receiver_map.is_constructor() != is_constructor || 2727 !InstanceTypeChecker::IsJSFunctionOrBoundFunctionOrWrappedFunction( 2728 receiver_map.instance_type())) { 2729 return inference.NoChange(); 2730 } 2731 2732 // Disallow binding of slow-mode functions. We need to figure out 2733 // whether the length and name property are in the original state. 2734 if (receiver_map.is_dictionary_map()) return inference.NoChange(); 2735 2736 // Check whether the length and name properties are still present 2737 // as AccessorInfo objects. In that case, their values can be 2738 // recomputed even if the actual value of the object changes. 2739 // This mirrors the checks done in builtins-function-gen.cc at 2740 // runtime otherwise. 2741 int minimum_nof_descriptors = 2742 std::max( 2743 {JSFunctionOrBoundFunctionOrWrappedFunction::kLengthDescriptorIndex, 2744 JSFunctionOrBoundFunctionOrWrappedFunction:: 2745 kNameDescriptorIndex}) + 2746 1; 2747 if (receiver_map.NumberOfOwnDescriptors() < minimum_nof_descriptors) { 2748 return inference.NoChange(); 2749 } 2750 const InternalIndex kLengthIndex( 2751 JSFunctionOrBoundFunctionOrWrappedFunction::kLengthDescriptorIndex); 2752 const InternalIndex kNameIndex( 2753 JSFunctionOrBoundFunctionOrWrappedFunction::kNameDescriptorIndex); 2754 ReadOnlyRoots roots(isolate()); 2755 StringRef length_string = MakeRef(broker(), roots.length_string_handle()); 2756 StringRef name_string = MakeRef(broker(), roots.name_string_handle()); 2757 2758 base::Optional<ObjectRef> length_value( 2759 receiver_map.GetStrongValue(kLengthIndex)); 2760 base::Optional<ObjectRef> name_value( 2761 receiver_map.GetStrongValue(kNameIndex)); 2762 if (!length_value || !name_value) { 2763 TRACE_BROKER_MISSING( 2764 broker(), "name or length descriptors on map " << receiver_map); 2765 return inference.NoChange(); 2766 } 2767 if (!receiver_map.GetPropertyKey(kLengthIndex).equals(length_string) || 2768 !length_value->IsAccessorInfo() || 2769 !receiver_map.GetPropertyKey(kNameIndex).equals(name_string) || 2770 !name_value->IsAccessorInfo()) { 2771 return inference.NoChange(); 2772 } 2773 } 2774 2775 // Choose the map for the resulting JSBoundFunction (but bail out in case of a 2776 // custom prototype). 2777 MapRef map = is_constructor 2778 ? native_context().bound_function_with_constructor_map() 2779 : native_context().bound_function_without_constructor_map(); 2780 if (!map.prototype().equals(prototype)) return inference.NoChange(); 2781 2782 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 2783 control, p.feedback()); 2784 2785 // Replace the {node} with a JSCreateBoundFunction. 2786 static constexpr int kBoundThis = 1; 2787 static constexpr int kReceiverContextEffectAndControl = 4; 2788 int const arity = n.ArgumentCount(); 2789 2790 if (arity > 0) { 2791 MapRef fixed_array_map = MakeRef(broker(), factory()->fixed_array_map()); 2792 AllocationBuilder ab(jsgraph(), effect, control); 2793 if (!ab.CanAllocateArray(arity, fixed_array_map)) { 2794 return NoChange(); 2795 } 2796 } 2797 2798 int const arity_with_bound_this = std::max(arity, kBoundThis); 2799 int const input_count = 2800 arity_with_bound_this + kReceiverContextEffectAndControl; 2801 Node** inputs = graph()->zone()->NewArray<Node*>(input_count); 2802 int cursor = 0; 2803 inputs[cursor++] = receiver; 2804 inputs[cursor++] = n.ArgumentOrUndefined(0, jsgraph()); // bound_this. 2805 for (int i = 1; i < arity; ++i) { 2806 inputs[cursor++] = n.Argument(i); 2807 } 2808 inputs[cursor++] = context; 2809 inputs[cursor++] = effect; 2810 inputs[cursor++] = control; 2811 DCHECK_EQ(cursor, input_count); 2812 Node* value = effect = 2813 graph()->NewNode(javascript()->CreateBoundFunction( 2814 arity_with_bound_this - kBoundThis, map), 2815 input_count, inputs); 2816 ReplaceWithValue(node, value, effect, control); 2817 return Replace(value); 2818} 2819 2820// ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args) 2821Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { 2822 JSCallNode n(node); 2823 CallParameters const& p = n.Parameters(); 2824 Node* target = n.target(); 2825 Effect effect = n.effect(); 2826 Control control = n.control(); 2827 2828 // Change context of {node} to the Function.prototype.call context, 2829 // to ensure any exception is thrown in the correct context. 2830 Node* context; 2831 HeapObjectMatcher m(target); 2832 if (m.HasResolvedValue() && m.Ref(broker()).IsJSFunction()) { 2833 JSFunctionRef function = m.Ref(broker()).AsJSFunction(); 2834 context = jsgraph()->Constant(function.context()); 2835 } else { 2836 context = effect = graph()->NewNode( 2837 simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target, 2838 effect, control); 2839 } 2840 NodeProperties::ReplaceContextInput(node, context); 2841 NodeProperties::ReplaceEffectInput(node, effect); 2842 2843 // Remove the target from {node} and use the receiver as target instead, and 2844 // the thisArg becomes the new target. If thisArg was not provided, insert 2845 // undefined instead. 2846 int arity = p.arity_without_implicit_args(); 2847 ConvertReceiverMode convert_mode; 2848 if (arity == 0) { 2849 // The thisArg was not provided, use undefined as receiver. 2850 convert_mode = ConvertReceiverMode::kNullOrUndefined; 2851 node->ReplaceInput(n.TargetIndex(), n.receiver()); 2852 node->ReplaceInput(n.ReceiverIndex(), jsgraph()->UndefinedConstant()); 2853 } else { 2854 // Just remove the target, which is the first value input. 2855 convert_mode = ConvertReceiverMode::kAny; 2856 node->RemoveInput(n.TargetIndex()); 2857 --arity; 2858 } 2859 NodeProperties::ChangeOp( 2860 node, javascript()->Call(JSCallNode::ArityForArgc(arity), p.frequency(), 2861 p.feedback(), convert_mode, p.speculation_mode(), 2862 CallFeedbackRelation::kUnrelated)); 2863 // Try to further reduce the JSCall {node}. 2864 return Changed(node).FollowedBy(ReduceJSCall(node)); 2865} 2866 2867// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V) 2868Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) { 2869 JSCallNode n(node); 2870 Node* receiver = n.receiver(); 2871 Node* object = n.ArgumentOrUndefined(0, jsgraph()); 2872 Node* context = n.context(); 2873 FrameState frame_state = n.frame_state(); 2874 Effect effect = n.effect(); 2875 Control control = n.control(); 2876 2877 // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the 2878 // stack trace doesn't contain the @@hasInstance call; we have the 2879 // corresponding bug in the baseline case. Some massaging of the frame 2880 // state would be necessary here. 2881 2882 // Morph this {node} into a JSOrdinaryHasInstance node. 2883 node->ReplaceInput(0, receiver); 2884 node->ReplaceInput(1, object); 2885 node->ReplaceInput(2, context); 2886 node->ReplaceInput(3, frame_state); 2887 node->ReplaceInput(4, effect); 2888 node->ReplaceInput(5, control); 2889 node->TrimInputCount(6); 2890 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); 2891 return Changed(node); 2892} 2893 2894Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) { 2895 Effect effect{NodeProperties::GetEffectInput(node)}; 2896 2897 // Try to determine the {object} map. 2898 MapInference inference(broker(), object, effect); 2899 if (!inference.HaveMaps()) return NoChange(); 2900 ZoneVector<MapRef> const& object_maps = inference.GetMaps(); 2901 2902 MapRef candidate_map = object_maps[0]; 2903 HeapObjectRef candidate_prototype = candidate_map.prototype(); 2904 2905 // Check if we can constant-fold the {candidate_prototype}. 2906 for (size_t i = 0; i < object_maps.size(); ++i) { 2907 MapRef object_map = object_maps[i]; 2908 HeapObjectRef map_prototype = object_map.prototype(); 2909 if (IsSpecialReceiverInstanceType(object_map.instance_type()) || 2910 !map_prototype.equals(candidate_prototype)) { 2911 // We exclude special receivers, like JSProxy or API objects that 2912 // might require access checks here; we also don't want to deal 2913 // with hidden prototypes at this point. 2914 return inference.NoChange(); 2915 } 2916 // The above check also excludes maps for primitive values, which is 2917 // important because we are not applying [[ToObject]] here as expected. 2918 DCHECK(!object_map.IsPrimitiveMap() && object_map.IsJSReceiverMap()); 2919 } 2920 if (!inference.RelyOnMapsViaStability(dependencies())) { 2921 return inference.NoChange(); 2922 } 2923 Node* value = jsgraph()->Constant(candidate_prototype); 2924 ReplaceWithValue(node, value); 2925 return Replace(value); 2926} 2927 2928// ES6 section 19.1.2.11 Object.getPrototypeOf ( O ) 2929Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node) { 2930 JSCallNode n(node); 2931 Node* object = n.ArgumentOrUndefined(0, jsgraph()); 2932 return ReduceObjectGetPrototype(node, object); 2933} 2934 2935// ES section #sec-object.is 2936Reduction JSCallReducer::ReduceObjectIs(Node* node) { 2937 JSCallNode n(node); 2938 Node* lhs = n.ArgumentOrUndefined(0, jsgraph()); 2939 Node* rhs = n.ArgumentOrUndefined(1, jsgraph()); 2940 Node* value = graph()->NewNode(simplified()->SameValue(), lhs, rhs); 2941 ReplaceWithValue(node, value); 2942 return Replace(value); 2943} 2944 2945// ES6 section B.2.2.1.1 get Object.prototype.__proto__ 2946Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) { 2947 JSCallNode n(node); 2948 return ReduceObjectGetPrototype(node, n.receiver()); 2949} 2950 2951// ES #sec-object.prototype.hasownproperty 2952Reduction JSCallReducer::ReduceObjectPrototypeHasOwnProperty(Node* node) { 2953 JSCallNode call_node(node); 2954 Node* receiver = call_node.receiver(); 2955 Node* name = call_node.ArgumentOrUndefined(0, jsgraph()); 2956 Effect effect = call_node.effect(); 2957 Control control = call_node.control(); 2958 2959 // We can optimize a call to Object.prototype.hasOwnProperty if it's being 2960 // used inside a fast-mode for..in, so for code like this: 2961 // 2962 // for (name in receiver) { 2963 // if (receiver.hasOwnProperty(name)) { 2964 // ... 2965 // } 2966 // } 2967 // 2968 // If the for..in is in fast-mode, we know that the {receiver} has {name} 2969 // as own property, otherwise the enumeration wouldn't include it. The graph 2970 // constructed by the BytecodeGraphBuilder in this case looks like this: 2971 2972 // receiver 2973 // ^ ^ 2974 // | | 2975 // | +-+ 2976 // | | 2977 // | JSToObject 2978 // | ^ 2979 // | | 2980 // | JSForInNext 2981 // | ^ 2982 // +----+ | 2983 // | | 2984 // JSCall[hasOwnProperty] 2985 2986 // We can constant-fold the {node} to True in this case, and insert 2987 // a (potentially redundant) map check to guard the fact that the 2988 // {receiver} map didn't change since the dominating JSForInNext. This 2989 // map check is only necessary when TurboFan cannot prove that there 2990 // is no observable side effect between the {JSForInNext} and the 2991 // {JSCall} to Object.prototype.hasOwnProperty. 2992 // 2993 // Also note that it's safe to look through the {JSToObject}, since the 2994 // Object.prototype.hasOwnProperty does an implicit ToObject anyway, and 2995 // these operations are not observable. 2996 if (name->opcode() == IrOpcode::kJSForInNext) { 2997 JSForInNextNode n(name); 2998 if (n.Parameters().mode() != ForInMode::kGeneric) { 2999 Node* object = n.receiver(); 3000 Node* cache_type = n.cache_type(); 3001 if (object->opcode() == IrOpcode::kJSToObject) { 3002 object = NodeProperties::GetValueInput(object, 0); 3003 } 3004 if (object == receiver) { 3005 // No need to repeat the map check if we can prove that there's no 3006 // observable side effect between {effect} and {name]. 3007 if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) { 3008 Node* receiver_map = effect = 3009 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), 3010 receiver, effect, control); 3011 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), 3012 receiver_map, cache_type); 3013 effect = graph()->NewNode( 3014 simplified()->CheckIf(DeoptimizeReason::kWrongMap), check, effect, 3015 control); 3016 } 3017 Node* value = jsgraph()->TrueConstant(); 3018 ReplaceWithValue(node, value, effect, control); 3019 return Replace(value); 3020 } 3021 } 3022 } 3023 3024 return NoChange(); 3025} 3026 3027// ES #sec-object.prototype.isprototypeof 3028Reduction JSCallReducer::ReduceObjectPrototypeIsPrototypeOf(Node* node) { 3029 JSCallNode n(node); 3030 Node* receiver = n.receiver(); 3031 Node* value = n.ArgumentOrUndefined(0, jsgraph()); 3032 Effect effect = n.effect(); 3033 3034 // Ensure that the {receiver} is known to be a JSReceiver (so that 3035 // the ToObject step of Object.prototype.isPrototypeOf is a no-op). 3036 MapInference inference(broker(), receiver, effect); 3037 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAreJSReceiver()) { 3038 return NoChange(); 3039 } 3040 3041 // We don't check whether {value} is a proper JSReceiver here explicitly, 3042 // and don't explicitly rule out Primitive {value}s, since all of them 3043 // have null as their prototype, so the prototype chain walk inside the 3044 // JSHasInPrototypeChain operator immediately aborts and yields false. 3045 NodeProperties::ReplaceValueInput(node, value, n.TargetIndex()); 3046 for (int i = node->op()->ValueInputCount(); i > 2; i--) { 3047 node->RemoveInput(2); 3048 } 3049 NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain()); 3050 return Changed(node); 3051} 3052 3053// ES6 section 26.1.1 Reflect.apply ( target, thisArgument, argumentsList ) 3054Reduction JSCallReducer::ReduceReflectApply(Node* node) { 3055 JSCallNode n(node); 3056 CallParameters const& p = n.Parameters(); 3057 int arity = p.arity_without_implicit_args(); 3058 // Massage value inputs appropriately. 3059 STATIC_ASSERT(n.ReceiverIndex() > n.TargetIndex()); 3060 node->RemoveInput(n.ReceiverIndex()); 3061 node->RemoveInput(n.TargetIndex()); 3062 while (arity < 3) { 3063 node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant()); 3064 } 3065 while (arity-- > 3) { 3066 node->RemoveInput(arity); 3067 } 3068 NodeProperties::ChangeOp( 3069 node, javascript()->CallWithArrayLike(p.frequency(), p.feedback(), 3070 p.speculation_mode(), 3071 CallFeedbackRelation::kUnrelated)); 3072 return Changed(node).FollowedBy(ReduceJSCallWithArrayLike(node)); 3073} 3074 3075// ES6 section 26.1.2 Reflect.construct ( target, argumentsList [, newTarget] ) 3076Reduction JSCallReducer::ReduceReflectConstruct(Node* node) { 3077 JSCallNode n(node); 3078 CallParameters const& p = n.Parameters(); 3079 int arity = p.arity_without_implicit_args(); 3080 // Massage value inputs appropriately. 3081 Node* arg_target = n.ArgumentOrUndefined(0, jsgraph()); 3082 Node* arg_argument_list = n.ArgumentOrUndefined(1, jsgraph()); 3083 Node* arg_new_target = n.ArgumentOr(2, arg_target); 3084 3085 STATIC_ASSERT(n.ReceiverIndex() > n.TargetIndex()); 3086 node->RemoveInput(n.ReceiverIndex()); 3087 node->RemoveInput(n.TargetIndex()); 3088 3089 // TODO(jgruber): This pattern essentially ensures that we have the correct 3090 // number of inputs for a given argument count. Wrap it in a helper function. 3091 STATIC_ASSERT(JSConstructNode::FirstArgumentIndex() == 2); 3092 while (arity < 3) { 3093 node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant()); 3094 } 3095 while (arity-- > 3) { 3096 node->RemoveInput(arity); 3097 } 3098 3099 STATIC_ASSERT(JSConstructNode::TargetIndex() == 0); 3100 STATIC_ASSERT(JSConstructNode::NewTargetIndex() == 1); 3101 STATIC_ASSERT(JSConstructNode::kFeedbackVectorIsLastInput); 3102 node->ReplaceInput(JSConstructNode::TargetIndex(), arg_target); 3103 node->ReplaceInput(JSConstructNode::NewTargetIndex(), arg_new_target); 3104 node->ReplaceInput(JSConstructNode::ArgumentIndex(0), arg_argument_list); 3105 3106 NodeProperties::ChangeOp( 3107 node, javascript()->ConstructWithArrayLike(p.frequency(), p.feedback())); 3108 return Changed(node).FollowedBy(ReduceJSConstructWithArrayLike(node)); 3109} 3110 3111// ES6 section 26.1.7 Reflect.getPrototypeOf ( target ) 3112Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { 3113 JSCallNode n(node); 3114 Node* target = n.ArgumentOrUndefined(0, jsgraph()); 3115 return ReduceObjectGetPrototype(node, target); 3116} 3117 3118// ES6 section #sec-object.create Object.create(proto, properties) 3119Reduction JSCallReducer::ReduceObjectCreate(Node* node) { 3120 JSCallNode n(node); 3121 Node* properties = n.ArgumentOrUndefined(1, jsgraph()); 3122 if (properties != jsgraph()->UndefinedConstant()) return NoChange(); 3123 3124 Node* context = n.context(); 3125 FrameState frame_state = n.frame_state(); 3126 Effect effect = n.effect(); 3127 Control control = n.control(); 3128 Node* prototype = n.ArgumentOrUndefined(0, jsgraph()); 3129 node->ReplaceInput(0, prototype); 3130 node->ReplaceInput(1, context); 3131 node->ReplaceInput(2, frame_state); 3132 node->ReplaceInput(3, effect); 3133 node->ReplaceInput(4, control); 3134 node->TrimInputCount(5); 3135 NodeProperties::ChangeOp(node, javascript()->CreateObject()); 3136 return Changed(node); 3137} 3138 3139// ES section #sec-reflect.get 3140Reduction JSCallReducer::ReduceReflectGet(Node* node) { 3141 JSCallNode n(node); 3142 CallParameters const& p = n.Parameters(); 3143 int arity = p.arity_without_implicit_args(); 3144 if (arity != 2) return NoChange(); 3145 Node* target = n.Argument(0); 3146 Node* key = n.Argument(1); 3147 Node* context = n.context(); 3148 FrameState frame_state = n.frame_state(); 3149 Effect effect = n.effect(); 3150 Control control = n.control(); 3151 3152 // Check whether {target} is a JSReceiver. 3153 Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target); 3154 Node* branch = 3155 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 3156 3157 // Throw an appropriate TypeError if the {target} is not a JSReceiver. 3158 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 3159 Node* efalse = effect; 3160 { 3161 if_false = efalse = graph()->NewNode( 3162 javascript()->CallRuntime(Runtime::kThrowTypeError, 2), 3163 jsgraph()->Constant( 3164 static_cast<int>(MessageTemplate::kCalledOnNonObject)), 3165 jsgraph()->HeapConstant(factory()->ReflectGet_string()), context, 3166 frame_state, efalse, if_false); 3167 } 3168 3169 // Otherwise just use the existing GetPropertyStub. 3170 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 3171 Node* etrue = effect; 3172 Node* vtrue; 3173 { 3174 Callable callable = Builtins::CallableFor(isolate(), Builtin::kGetProperty); 3175 auto call_descriptor = Linkage::GetStubCallDescriptor( 3176 graph()->zone(), callable.descriptor(), 3177 callable.descriptor().GetStackParameterCount(), 3178 CallDescriptor::kNeedsFrameState, Operator::kNoProperties); 3179 Node* stub_code = jsgraph()->HeapConstant(callable.code()); 3180 vtrue = etrue = if_true = 3181 graph()->NewNode(common()->Call(call_descriptor), stub_code, target, 3182 key, context, frame_state, etrue, if_true); 3183 } 3184 3185 // Rewire potential exception edges. 3186 Node* on_exception = nullptr; 3187 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 3188 // Create appropriate {IfException} and {IfSuccess} nodes. 3189 Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true); 3190 if_true = graph()->NewNode(common()->IfSuccess(), if_true); 3191 Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false); 3192 if_false = graph()->NewNode(common()->IfSuccess(), if_false); 3193 3194 // Join the exception edges. 3195 Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse); 3196 Node* ephi = 3197 graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge); 3198 Node* phi = 3199 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 3200 extrue, exfalse, merge); 3201 ReplaceWithValue(on_exception, phi, ephi, merge); 3202 } 3203 3204 // Connect the throwing path to end. 3205 if_false = graph()->NewNode(common()->Throw(), efalse, if_false); 3206 NodeProperties::MergeControlToEnd(graph(), common(), if_false); 3207 3208 // Continue on the regular path. 3209 ReplaceWithValue(node, vtrue, etrue, if_true); 3210 return Changed(vtrue); 3211} 3212 3213// ES section #sec-reflect.has 3214Reduction JSCallReducer::ReduceReflectHas(Node* node) { 3215 JSCallNode n(node); 3216 Node* target = n.ArgumentOrUndefined(0, jsgraph()); 3217 Node* key = n.ArgumentOrUndefined(1, jsgraph()); 3218 Node* context = n.context(); 3219 Effect effect = n.effect(); 3220 Control control = n.control(); 3221 FrameState frame_state = n.frame_state(); 3222 3223 // Check whether {target} is a JSReceiver. 3224 Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target); 3225 Node* branch = 3226 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 3227 3228 // Throw an appropriate TypeError if the {target} is not a JSReceiver. 3229 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 3230 Node* efalse = effect; 3231 { 3232 if_false = efalse = graph()->NewNode( 3233 javascript()->CallRuntime(Runtime::kThrowTypeError, 2), 3234 jsgraph()->Constant( 3235 static_cast<int>(MessageTemplate::kCalledOnNonObject)), 3236 jsgraph()->HeapConstant(factory()->ReflectHas_string()), context, 3237 frame_state, efalse, if_false); 3238 } 3239 3240 // Otherwise just use the existing {JSHasProperty} logic. 3241 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 3242 Node* etrue = effect; 3243 Node* vtrue; 3244 { 3245 // TODO(magardn): collect feedback so this can be optimized 3246 vtrue = etrue = if_true = graph()->NewNode( 3247 javascript()->HasProperty(FeedbackSource()), target, key, 3248 jsgraph()->UndefinedConstant(), context, frame_state, etrue, if_true); 3249 } 3250 3251 // Rewire potential exception edges. 3252 Node* on_exception = nullptr; 3253 if (NodeProperties::IsExceptionalCall(node, &on_exception)) { 3254 // Create appropriate {IfException} and {IfSuccess} nodes. 3255 Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true); 3256 if_true = graph()->NewNode(common()->IfSuccess(), if_true); 3257 Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false); 3258 if_false = graph()->NewNode(common()->IfSuccess(), if_false); 3259 3260 // Join the exception edges. 3261 Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse); 3262 Node* ephi = 3263 graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge); 3264 Node* phi = 3265 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 3266 extrue, exfalse, merge); 3267 ReplaceWithValue(on_exception, phi, ephi, merge); 3268 } 3269 3270 // Connect the throwing path to end. 3271 if_false = graph()->NewNode(common()->Throw(), efalse, if_false); 3272 NodeProperties::MergeControlToEnd(graph(), common(), if_false); 3273 3274 // Continue on the regular path. 3275 ReplaceWithValue(node, vtrue, etrue, if_true); 3276 return Changed(vtrue); 3277} 3278 3279namespace { 3280 3281bool CanInlineArrayIteratingBuiltin(JSHeapBroker* broker, 3282 ZoneVector<MapRef> const& receiver_maps, 3283 ElementsKind* kind_return) { 3284 DCHECK_NE(0, receiver_maps.size()); 3285 *kind_return = receiver_maps[0].elements_kind(); 3286 for (const MapRef& map : receiver_maps) { 3287 if (!map.supports_fast_array_iteration() || 3288 !UnionElementsKindUptoSize(kind_return, map.elements_kind())) { 3289 return false; 3290 } 3291 } 3292 return true; 3293} 3294 3295bool CanInlineArrayResizingBuiltin(JSHeapBroker* broker, 3296 ZoneVector<MapRef> const& receiver_maps, 3297 std::vector<ElementsKind>* kinds, 3298 bool builtin_is_push = false) { 3299 DCHECK_NE(0, receiver_maps.size()); 3300 for (const MapRef& map : receiver_maps) { 3301 if (!map.supports_fast_array_resize()) return false; 3302 // TODO(turbofan): We should also handle fast holey double elements once 3303 // we got the hole NaN mess sorted out in TurboFan/V8. 3304 if (map.elements_kind() == HOLEY_DOUBLE_ELEMENTS && !builtin_is_push) { 3305 return false; 3306 } 3307 ElementsKind current_kind = map.elements_kind(); 3308 auto kind_ptr = kinds->data(); 3309 size_t i; 3310 for (i = 0; i < kinds->size(); i++, kind_ptr++) { 3311 if (UnionElementsKindUptoPackedness(kind_ptr, current_kind)) { 3312 break; 3313 } 3314 } 3315 if (i == kinds->size()) kinds->push_back(current_kind); 3316 } 3317 return true; 3318} 3319 3320// Wraps common setup code for iterating array builtins. 3321class IteratingArrayBuiltinHelper { 3322 public: 3323 IteratingArrayBuiltinHelper(Node* node, JSHeapBroker* broker, 3324 JSGraph* jsgraph, 3325 CompilationDependencies* dependencies) 3326 : receiver_(NodeProperties::GetValueInput(node, 1)), 3327 effect_(NodeProperties::GetEffectInput(node)), 3328 control_(NodeProperties::GetControlInput(node)), 3329 inference_(broker, receiver_, effect_) { 3330 if (!FLAG_turbo_inline_array_builtins) return; 3331 3332 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 3333 const CallParameters& p = CallParametersOf(node->op()); 3334 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 3335 return; 3336 } 3337 3338 // Try to determine the {receiver} map. 3339 if (!inference_.HaveMaps()) return; 3340 ZoneVector<MapRef> const& receiver_maps = inference_.GetMaps(); 3341 3342 if (!CanInlineArrayIteratingBuiltin(broker, receiver_maps, 3343 &elements_kind_)) { 3344 return; 3345 } 3346 3347 // TODO(jgruber): May only be needed for holey elements kinds. 3348 if (!dependencies->DependOnNoElementsProtector()) return; 3349 3350 has_stability_dependency_ = inference_.RelyOnMapsPreferStability( 3351 dependencies, jsgraph, &effect_, control_, p.feedback()); 3352 3353 can_reduce_ = true; 3354 } 3355 3356 bool can_reduce() const { return can_reduce_; } 3357 bool has_stability_dependency() const { return has_stability_dependency_; } 3358 Effect effect() const { return effect_; } 3359 Control control() const { return control_; } 3360 MapInference* inference() { return &inference_; } 3361 ElementsKind elements_kind() const { return elements_kind_; } 3362 3363 private: 3364 bool can_reduce_ = false; 3365 bool has_stability_dependency_ = false; 3366 Node* receiver_; 3367 Effect effect_; 3368 Control control_; 3369 MapInference inference_; 3370 ElementsKind elements_kind_; 3371}; 3372 3373} // namespace 3374 3375Reduction JSCallReducer::ReduceArrayForEach( 3376 Node* node, const SharedFunctionInfoRef& shared) { 3377 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3378 if (!h.can_reduce()) return h.inference()->NoChange(); 3379 3380 IteratingArrayBuiltinReducerAssembler a(this, node); 3381 a.InitializeEffectControl(h.effect(), h.control()); 3382 TNode<Object> subgraph = a.ReduceArrayPrototypeForEach( 3383 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared); 3384 return ReplaceWithSubgraph(&a, subgraph); 3385} 3386 3387Reduction JSCallReducer::ReduceArrayReduce( 3388 Node* node, const SharedFunctionInfoRef& shared) { 3389 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3390 if (!h.can_reduce()) return h.inference()->NoChange(); 3391 3392 IteratingArrayBuiltinReducerAssembler a(this, node); 3393 a.InitializeEffectControl(h.effect(), h.control()); 3394 TNode<Object> subgraph = a.ReduceArrayPrototypeReduce( 3395 h.inference(), h.has_stability_dependency(), h.elements_kind(), 3396 ArrayReduceDirection::kLeft, shared); 3397 return ReplaceWithSubgraph(&a, subgraph); 3398} 3399 3400Reduction JSCallReducer::ReduceArrayReduceRight( 3401 Node* node, const SharedFunctionInfoRef& shared) { 3402 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3403 if (!h.can_reduce()) return h.inference()->NoChange(); 3404 3405 IteratingArrayBuiltinReducerAssembler a(this, node); 3406 a.InitializeEffectControl(h.effect(), h.control()); 3407 TNode<Object> subgraph = a.ReduceArrayPrototypeReduce( 3408 h.inference(), h.has_stability_dependency(), h.elements_kind(), 3409 ArrayReduceDirection::kRight, shared); 3410 return ReplaceWithSubgraph(&a, subgraph); 3411} 3412 3413Reduction JSCallReducer::ReduceArrayMap(Node* node, 3414 const SharedFunctionInfoRef& shared) { 3415 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3416 if (!h.can_reduce()) return h.inference()->NoChange(); 3417 3418 // Calls CreateArray and thus requires this additional protector dependency. 3419 if (!dependencies()->DependOnArraySpeciesProtector()) { 3420 return h.inference()->NoChange(); 3421 } 3422 3423 IteratingArrayBuiltinReducerAssembler a(this, node); 3424 a.InitializeEffectControl(h.effect(), h.control()); 3425 3426 TNode<Object> subgraph = 3427 a.ReduceArrayPrototypeMap(h.inference(), h.has_stability_dependency(), 3428 h.elements_kind(), shared, native_context()); 3429 return ReplaceWithSubgraph(&a, subgraph); 3430} 3431 3432Reduction JSCallReducer::ReduceArrayFilter( 3433 Node* node, const SharedFunctionInfoRef& shared) { 3434 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3435 if (!h.can_reduce()) return h.inference()->NoChange(); 3436 3437 // Calls CreateArray and thus requires this additional protector dependency. 3438 if (!dependencies()->DependOnArraySpeciesProtector()) { 3439 return h.inference()->NoChange(); 3440 } 3441 3442 IteratingArrayBuiltinReducerAssembler a(this, node); 3443 a.InitializeEffectControl(h.effect(), h.control()); 3444 3445 TNode<Object> subgraph = 3446 a.ReduceArrayPrototypeFilter(h.inference(), h.has_stability_dependency(), 3447 h.elements_kind(), shared, native_context()); 3448 return ReplaceWithSubgraph(&a, subgraph); 3449} 3450 3451Reduction JSCallReducer::ReduceArrayFind(Node* node, 3452 const SharedFunctionInfoRef& shared) { 3453 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3454 if (!h.can_reduce()) return h.inference()->NoChange(); 3455 3456 IteratingArrayBuiltinReducerAssembler a(this, node); 3457 a.InitializeEffectControl(h.effect(), h.control()); 3458 3459 TNode<Object> subgraph = a.ReduceArrayPrototypeFind( 3460 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared, 3461 native_context(), ArrayFindVariant::kFind); 3462 return ReplaceWithSubgraph(&a, subgraph); 3463} 3464 3465Reduction JSCallReducer::ReduceArrayFindIndex( 3466 Node* node, const SharedFunctionInfoRef& shared) { 3467 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3468 if (!h.can_reduce()) return h.inference()->NoChange(); 3469 3470 IteratingArrayBuiltinReducerAssembler a(this, node); 3471 a.InitializeEffectControl(h.effect(), h.control()); 3472 3473 TNode<Object> subgraph = a.ReduceArrayPrototypeFind( 3474 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared, 3475 native_context(), ArrayFindVariant::kFindIndex); 3476 return ReplaceWithSubgraph(&a, subgraph); 3477} 3478 3479Reduction JSCallReducer::ReduceArrayEvery(Node* node, 3480 const SharedFunctionInfoRef& shared) { 3481 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3482 if (!h.can_reduce()) return h.inference()->NoChange(); 3483 3484 IteratingArrayBuiltinReducerAssembler a(this, node); 3485 a.InitializeEffectControl(h.effect(), h.control()); 3486 3487 TNode<Object> subgraph = a.ReduceArrayPrototypeEverySome( 3488 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared, 3489 native_context(), ArrayEverySomeVariant::kEvery); 3490 return ReplaceWithSubgraph(&a, subgraph); 3491} 3492 3493// ES7 Array.prototype.inludes(searchElement[, fromIndex]) 3494// #sec-array.prototype.includes 3495Reduction JSCallReducer::ReduceArrayIncludes(Node* node) { 3496 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3497 if (!h.can_reduce()) return h.inference()->NoChange(); 3498 3499 IteratingArrayBuiltinReducerAssembler a(this, node); 3500 a.InitializeEffectControl(h.effect(), h.control()); 3501 3502 TNode<Object> subgraph = a.ReduceArrayPrototypeIndexOfIncludes( 3503 h.elements_kind(), ArrayIndexOfIncludesVariant::kIncludes); 3504 return ReplaceWithSubgraph(&a, subgraph); 3505} 3506 3507// ES6 Array.prototype.indexOf(searchElement[, fromIndex]) 3508// #sec-array.prototype.indexof 3509Reduction JSCallReducer::ReduceArrayIndexOf(Node* node) { 3510 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3511 if (!h.can_reduce()) return h.inference()->NoChange(); 3512 3513 IteratingArrayBuiltinReducerAssembler a(this, node); 3514 a.InitializeEffectControl(h.effect(), h.control()); 3515 3516 TNode<Object> subgraph = a.ReduceArrayPrototypeIndexOfIncludes( 3517 h.elements_kind(), ArrayIndexOfIncludesVariant::kIndexOf); 3518 return ReplaceWithSubgraph(&a, subgraph); 3519} 3520 3521Reduction JSCallReducer::ReduceArraySome(Node* node, 3522 const SharedFunctionInfoRef& shared) { 3523 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies()); 3524 if (!h.can_reduce()) return h.inference()->NoChange(); 3525 3526 IteratingArrayBuiltinReducerAssembler a(this, node); 3527 a.InitializeEffectControl(h.effect(), h.control()); 3528 3529 TNode<Object> subgraph = a.ReduceArrayPrototypeEverySome( 3530 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared, 3531 native_context(), ArrayEverySomeVariant::kSome); 3532 return ReplaceWithSubgraph(&a, subgraph); 3533} 3534 3535#if V8_ENABLE_WEBASSEMBLY 3536 3537namespace { 3538 3539bool CanInlineJSToWasmCall(const wasm::FunctionSig* wasm_signature) { 3540 if (wasm_signature->return_count() > 1) { 3541 return false; 3542 } 3543 3544 for (auto type : wasm_signature->all()) { 3545#if defined(V8_TARGET_ARCH_32_BIT) 3546 if (type == wasm::kWasmI64) return false; 3547#endif 3548 if (type != wasm::kWasmI32 && type != wasm::kWasmI64 && 3549 type != wasm::kWasmF32 && type != wasm::kWasmF64) { 3550 return false; 3551 } 3552 } 3553 3554 return true; 3555} 3556 3557} // namespace 3558 3559Reduction JSCallReducer::ReduceCallWasmFunction( 3560 Node* node, const SharedFunctionInfoRef& shared) { 3561 DCHECK(flags() & kInlineJSToWasmCalls); 3562 3563 JSCallNode n(node); 3564 const CallParameters& p = n.Parameters(); 3565 3566 // Avoid deoptimization loops 3567 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 3568 return NoChange(); 3569 } 3570 3571 const wasm::FunctionSig* wasm_signature = shared.wasm_function_signature(); 3572 if (!CanInlineJSToWasmCall(wasm_signature)) { 3573 return NoChange(); 3574 } 3575 3576 // Signal TurboFan that it should run the 'wasm-inlining' phase. 3577 has_wasm_calls_ = true; 3578 3579 const wasm::WasmModule* wasm_module = shared.wasm_module(); 3580 const Operator* op = 3581 javascript()->CallWasm(wasm_module, wasm_signature, p.feedback()); 3582 3583 // Remove additional inputs 3584 size_t actual_arity = n.ArgumentCount(); 3585 DCHECK(JSCallNode::kFeedbackVectorIsLastInput); 3586 DCHECK_EQ(actual_arity + JSWasmCallNode::kExtraInputCount - 1, 3587 n.FeedbackVectorIndex()); 3588 size_t expected_arity = wasm_signature->parameter_count(); 3589 3590 while (actual_arity > expected_arity) { 3591 int removal_index = 3592 static_cast<int>(n.FirstArgumentIndex() + expected_arity); 3593 DCHECK_LT(removal_index, static_cast<int>(node->InputCount())); 3594 node->RemoveInput(removal_index); 3595 actual_arity--; 3596 } 3597 3598 // Add missing inputs 3599 while (actual_arity < expected_arity) { 3600 int insertion_index = n.ArgumentIndex(n.ArgumentCount()); 3601 node->InsertInput(graph()->zone(), insertion_index, 3602 jsgraph()->UndefinedConstant()); 3603 actual_arity++; 3604 } 3605 3606 NodeProperties::ChangeOp(node, op); 3607 return Changed(node); 3608} 3609#endif // V8_ENABLE_WEBASSEMBLY 3610 3611// Given a FunctionTemplateInfo, checks whether the fast API call can be 3612// optimized, applying the initial step of the overload resolution algorithm: 3613// Given an overload set function_template_info.c_signatures, and a list of 3614// arguments of size argc: 3615// 1. Let max_arg be the length of the longest type list of the entries in 3616// function_template_info.c_signatures. 3617// 2. Let argc be the size of the arguments list. 3618// 3. Initialize arg_count = min(max_arg, argc). 3619// 4. Remove from the set all entries whose type list is not of length 3620// arg_count. 3621// Returns an array with the indexes of the remaining entries in S, which 3622// represents the set of "optimizable" function overloads. 3623 3624FastApiCallFunctionVector CanOptimizeFastCall( 3625 Zone* zone, const FunctionTemplateInfoRef& function_template_info, 3626 size_t argc) { 3627 FastApiCallFunctionVector result(zone); 3628 if (!FLAG_turbo_fast_api_calls) return result; 3629 3630 static constexpr int kReceiver = 1; 3631 3632 ZoneVector<Address> functions = function_template_info.c_functions(); 3633 ZoneVector<const CFunctionInfo*> signatures = 3634 function_template_info.c_signatures(); 3635 const size_t overloads_count = signatures.size(); 3636 3637 // Calculates the length of the longest type list of the entries in 3638 // function_template_info. 3639 size_t max_arg = 0; 3640 for (size_t i = 0; i < overloads_count; i++) { 3641 const CFunctionInfo* c_signature = signatures[i]; 3642 // C arguments should include the receiver at index 0. 3643 DCHECK_GE(c_signature->ArgumentCount(), kReceiver); 3644 const size_t len = c_signature->ArgumentCount() - kReceiver; 3645 if (len > max_arg) max_arg = len; 3646 } 3647 const size_t arg_count = std::min(max_arg, argc); 3648 3649 // Only considers entries whose type list length matches arg_count. 3650 for (size_t i = 0; i < overloads_count; i++) { 3651 const CFunctionInfo* c_signature = signatures[i]; 3652 const size_t len = c_signature->ArgumentCount() - kReceiver; 3653 bool optimize_to_fast_call = (len == arg_count); 3654 3655 optimize_to_fast_call = 3656 optimize_to_fast_call && 3657 fast_api_call::CanOptimizeFastSignature(c_signature); 3658 3659 if (optimize_to_fast_call) { 3660 result.push_back({functions[i], c_signature}); 3661 } 3662 } 3663 3664 return result; 3665} 3666 3667Reduction JSCallReducer::ReduceCallApiFunction( 3668 Node* node, const SharedFunctionInfoRef& shared) { 3669 JSCallNode n(node); 3670 CallParameters const& p = n.Parameters(); 3671 int const argc = p.arity_without_implicit_args(); 3672 Node* target = n.target(); 3673 Node* global_proxy = 3674 jsgraph()->Constant(native_context().global_proxy_object()); 3675 Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined) 3676 ? global_proxy 3677 : n.receiver(); 3678 Node* holder; 3679 Node* context = n.context(); 3680 Effect effect = n.effect(); 3681 Control control = n.control(); 3682 FrameState frame_state = n.frame_state(); 3683 3684 if (!shared.function_template_info().has_value()) { 3685 TRACE_BROKER_MISSING( 3686 broker(), "FunctionTemplateInfo for function with SFI " << shared); 3687 return NoChange(); 3688 } 3689 3690 // See if we can optimize this API call to {shared}. 3691 FunctionTemplateInfoRef function_template_info( 3692 shared.function_template_info().value()); 3693 3694 if (function_template_info.accept_any_receiver() && 3695 function_template_info.is_signature_undefined()) { 3696 // We might be able to 3697 // optimize the API call depending on the {function_template_info}. 3698 // If the API function accepts any kind of {receiver}, we only need to 3699 // ensure that the {receiver} is actually a JSReceiver at this point, 3700 // and also pass that as the {holder}. There are two independent bits 3701 // here: 3702 // 3703 // a. When the "accept any receiver" bit is set, it means we don't 3704 // need to perform access checks, even if the {receiver}'s map 3705 // has the "needs access check" bit set. 3706 // b. When the {function_template_info} has no signature, we don't 3707 // need to do the compatible receiver check, since all receivers 3708 // are considered compatible at that point, and the {receiver} 3709 // will be pass as the {holder}. 3710 // 3711 receiver = holder = effect = 3712 graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()), 3713 receiver, global_proxy, effect, control); 3714 } else { 3715 // Try to infer the {receiver} maps from the graph. 3716 MapInference inference(broker(), receiver, effect); 3717 if (inference.HaveMaps()) { 3718 ZoneVector<MapRef> const& receiver_maps = inference.GetMaps(); 3719 MapRef first_receiver_map = receiver_maps[0]; 3720 3721 // See if we can constant-fold the compatible receiver checks. 3722 HolderLookupResult api_holder = 3723 function_template_info.LookupHolderOfExpectedType(first_receiver_map); 3724 if (api_holder.lookup == CallOptimization::kHolderNotFound) { 3725 return inference.NoChange(); 3726 } 3727 3728 // Check that all {receiver_maps} are actually JSReceiver maps and 3729 // that the {function_template_info} accepts them without access 3730 // checks (even if "access check needed" is set for {receiver}). 3731 // 3732 // Note that we don't need to know the concrete {receiver} maps here, 3733 // meaning it's fine if the {receiver_maps} are unreliable, and we also 3734 // don't need to install any stability dependencies, since the only 3735 // relevant information regarding the {receiver} is the Map::constructor 3736 // field on the root map (which is different from the JavaScript exposed 3737 // "constructor" property) and that field cannot change. 3738 // 3739 // So if we know that {receiver} had a certain constructor at some point 3740 // in the past (i.e. it had a certain map), then this constructor is going 3741 // to be the same later, since this information cannot change with map 3742 // transitions. 3743 // 3744 // The same is true for the instance type, e.g. we still know that the 3745 // instance type is JSObject even if that information is unreliable, and 3746 // the "access check needed" bit, which also cannot change later. 3747 CHECK(first_receiver_map.IsJSReceiverMap()); 3748 CHECK(!first_receiver_map.is_access_check_needed() || 3749 function_template_info.accept_any_receiver()); 3750 3751 for (size_t i = 1; i < receiver_maps.size(); ++i) { 3752 MapRef receiver_map = receiver_maps[i]; 3753 HolderLookupResult holder_i = 3754 function_template_info.LookupHolderOfExpectedType(receiver_map); 3755 3756 if (api_holder.lookup != holder_i.lookup) return inference.NoChange(); 3757 DCHECK(holder_i.lookup == CallOptimization::kHolderFound || 3758 holder_i.lookup == CallOptimization::kHolderIsReceiver); 3759 if (holder_i.lookup == CallOptimization::kHolderFound) { 3760 DCHECK(api_holder.holder.has_value() && holder_i.holder.has_value()); 3761 if (!api_holder.holder->equals(*holder_i.holder)) { 3762 return inference.NoChange(); 3763 } 3764 } 3765 3766 CHECK(receiver_map.IsJSReceiverMap()); 3767 CHECK(!receiver_map.is_access_check_needed() || 3768 function_template_info.accept_any_receiver()); 3769 } 3770 3771 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation && 3772 !inference.RelyOnMapsViaStability(dependencies())) { 3773 // We were not able to make the receiver maps reliable without map 3774 // checks but doing map checks would lead to deopt loops, so give up. 3775 return inference.NoChange(); 3776 } 3777 3778 // TODO(neis): The maps were used in a way that does not actually require 3779 // map checks or stability dependencies. 3780 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 3781 control, p.feedback()); 3782 3783 // Determine the appropriate holder for the {lookup}. 3784 holder = api_holder.lookup == CallOptimization::kHolderFound 3785 ? jsgraph()->Constant(*api_holder.holder) 3786 : receiver; 3787 } else { 3788 // We don't have enough information to eliminate the access check 3789 // and/or the compatible receiver check, so use the generic builtin 3790 // that does those checks dynamically. This is still significantly 3791 // faster than the generic call sequence. 3792 Builtin builtin_name; 3793 if (function_template_info.accept_any_receiver()) { 3794 builtin_name = Builtin::kCallFunctionTemplate_CheckCompatibleReceiver; 3795 } else if (function_template_info.is_signature_undefined()) { 3796 builtin_name = Builtin::kCallFunctionTemplate_CheckAccess; 3797 } else { 3798 builtin_name = 3799 Builtin::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver; 3800 } 3801 3802 // The CallFunctionTemplate builtin requires the {receiver} to be 3803 // an actual JSReceiver, so make sure we do the proper conversion 3804 // first if necessary. 3805 receiver = holder = effect = 3806 graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()), 3807 receiver, global_proxy, effect, control); 3808 3809 Callable callable = Builtins::CallableFor(isolate(), builtin_name); 3810 auto call_descriptor = Linkage::GetStubCallDescriptor( 3811 graph()->zone(), callable.descriptor(), 3812 argc + 1 /* implicit receiver */, CallDescriptor::kNeedsFrameState); 3813 node->RemoveInput(n.FeedbackVectorIndex()); 3814 node->InsertInput(graph()->zone(), 0, 3815 jsgraph()->HeapConstant(callable.code())); 3816 node->ReplaceInput(1, jsgraph()->Constant(function_template_info)); 3817 node->InsertInput(graph()->zone(), 2, 3818 jsgraph()->Constant(JSParameterCount(argc))); 3819 node->ReplaceInput(3, receiver); // Update receiver input. 3820 node->ReplaceInput(6 + argc, effect); // Update effect input. 3821 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); 3822 return Changed(node); 3823 } 3824 } 3825 3826 // TODO(turbofan): Consider introducing a JSCallApiCallback operator for 3827 // this and lower it during JSGenericLowering, and unify this with the 3828 // JSNativeContextSpecialization::InlineApiCall method a bit. 3829 if (!function_template_info.call_code().has_value()) { 3830 TRACE_BROKER_MISSING(broker(), "call code for function template info " 3831 << function_template_info); 3832 return NoChange(); 3833 } 3834 3835 // Handles overloaded functions. 3836 3837 FastApiCallFunctionVector c_candidate_functions = 3838 CanOptimizeFastCall(graph()->zone(), function_template_info, argc); 3839 DCHECK_LE(c_candidate_functions.size(), 2); 3840 3841 if (!c_candidate_functions.empty()) { 3842 FastApiCallReducerAssembler a(this, node, function_template_info, 3843 c_candidate_functions, receiver, holder, 3844 shared, target, argc, effect); 3845 Node* fast_call_subgraph = a.ReduceFastApiCall(); 3846 ReplaceWithSubgraph(&a, fast_call_subgraph); 3847 3848 return Replace(fast_call_subgraph); 3849 } 3850 3851 // Slow call 3852 3853 CallHandlerInfoRef call_handler_info = *function_template_info.call_code(); 3854 Callable call_api_callback = CodeFactory::CallApiCallback(isolate()); 3855 CallInterfaceDescriptor cid = call_api_callback.descriptor(); 3856 auto call_descriptor = 3857 Linkage::GetStubCallDescriptor(graph()->zone(), cid, argc + 1 /* 3858 implicit receiver */, CallDescriptor::kNeedsFrameState); 3859 ApiFunction api_function(call_handler_info.callback()); 3860 ExternalReference function_reference = ExternalReference::Create( 3861 &api_function, ExternalReference::DIRECT_API_CALL); 3862 3863 Node* continuation_frame_state = CreateGenericLazyDeoptContinuationFrameState( 3864 jsgraph(), shared, target, context, receiver, frame_state); 3865 3866 node->RemoveInput(n.FeedbackVectorIndex()); 3867 node->InsertInput(graph()->zone(), 0, 3868 jsgraph()->HeapConstant(call_api_callback.code())); 3869 node->ReplaceInput(1, jsgraph()->ExternalConstant(function_reference)); 3870 node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(argc)); 3871 node->InsertInput(graph()->zone(), 3, 3872 jsgraph()->Constant(call_handler_info.data())); 3873 node->InsertInput(graph()->zone(), 4, holder); 3874 node->ReplaceInput(5, receiver); // Update receiver input. 3875 // 6 + argc is context input. 3876 node->ReplaceInput(6 + argc + 1, continuation_frame_state); 3877 node->ReplaceInput(6 + argc + 2, effect); 3878 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); 3879 return Changed(node); 3880} 3881 3882namespace { 3883 3884// Check whether elements aren't mutated; we play it extremely safe here by 3885// explicitly checking that {node} is only used by {LoadField} or 3886// {LoadElement}. 3887bool IsSafeArgumentsElements(Node* node) { 3888 for (Edge const edge : node->use_edges()) { 3889 if (!NodeProperties::IsValueEdge(edge)) continue; 3890 if (edge.from()->opcode() != IrOpcode::kLoadField && 3891 edge.from()->opcode() != IrOpcode::kLoadElement) { 3892 return false; 3893 } 3894 } 3895 return true; 3896} 3897 3898#ifdef DEBUG 3899bool IsCallOrConstructWithArrayLike(Node* node) { 3900 return node->opcode() == IrOpcode::kJSCallWithArrayLike || 3901 node->opcode() == IrOpcode::kJSConstructWithArrayLike; 3902} 3903#endif 3904 3905bool IsCallOrConstructWithSpread(Node* node) { 3906 return node->opcode() == IrOpcode::kJSCallWithSpread || 3907 node->opcode() == IrOpcode::kJSConstructWithSpread; 3908} 3909 3910bool IsCallWithArrayLikeOrSpread(Node* node) { 3911 return node->opcode() == IrOpcode::kJSCallWithArrayLike || 3912 node->opcode() == IrOpcode::kJSCallWithSpread; 3913} 3914 3915} // namespace 3916 3917void JSCallReducer::CheckIfConstructor(Node* construct) { 3918 JSConstructNode n(construct); 3919 Node* new_target = n.new_target(); 3920 Control control = n.control(); 3921 3922 Node* check = 3923 graph()->NewNode(simplified()->ObjectIsConstructor(), new_target); 3924 Node* check_branch = 3925 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 3926 Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch); 3927 Node* check_throw = check_fail = graph()->NewNode( 3928 javascript()->CallRuntime(Runtime::kThrowTypeError, 2), 3929 jsgraph()->Constant(static_cast<int>(MessageTemplate::kNotConstructor)), 3930 new_target, n.context(), n.frame_state(), n.effect(), check_fail); 3931 control = graph()->NewNode(common()->IfTrue(), check_branch); 3932 NodeProperties::ReplaceControlInput(construct, control); 3933 3934 // Rewire potential exception edges. 3935 Node* on_exception = nullptr; 3936 if (NodeProperties::IsExceptionalCall(construct, &on_exception)) { 3937 // Create appropriate {IfException} and {IfSuccess} nodes. 3938 Node* if_exception = 3939 graph()->NewNode(common()->IfException(), check_throw, check_fail); 3940 check_fail = graph()->NewNode(common()->IfSuccess(), check_fail); 3941 3942 // Join the exception edges. 3943 Node* merge = 3944 graph()->NewNode(common()->Merge(2), if_exception, on_exception); 3945 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception, 3946 on_exception, merge); 3947 Node* phi = 3948 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 3949 if_exception, on_exception, merge); 3950 ReplaceWithValue(on_exception, phi, ephi, merge); 3951 merge->ReplaceInput(1, on_exception); 3952 ephi->ReplaceInput(1, on_exception); 3953 phi->ReplaceInput(1, on_exception); 3954 } 3955 3956 // The above %ThrowTypeError runtime call is an unconditional throw, 3957 // making it impossible to return a successful completion in this case. We 3958 // simply connect the successful completion to the graph end. 3959 Node* throw_node = 3960 graph()->NewNode(common()->Throw(), check_throw, check_fail); 3961 NodeProperties::MergeControlToEnd(graph(), common(), throw_node); 3962} 3963 3964namespace { 3965 3966bool ShouldUseCallICFeedback(Node* node) { 3967 HeapObjectMatcher m(node); 3968 if (m.HasResolvedValue() || m.IsCheckClosure() || m.IsJSCreateClosure()) { 3969 // Don't use CallIC feedback when we know the function 3970 // being called, i.e. either know the closure itself or 3971 // at least the SharedFunctionInfo. 3972 return false; 3973 } else if (m.IsPhi()) { 3974 // Protect against endless loops here. 3975 Node* control = NodeProperties::GetControlInput(node); 3976 if (control->opcode() == IrOpcode::kLoop || 3977 control->opcode() == IrOpcode::kDead) 3978 return false; 3979 // Check if {node} is a Phi of nodes which shouldn't 3980 // use CallIC feedback (not looking through loops). 3981 int const value_input_count = m.node()->op()->ValueInputCount(); 3982 for (int n = 0; n < value_input_count; ++n) { 3983 if (ShouldUseCallICFeedback(node->InputAt(n))) return true; 3984 } 3985 return false; 3986 } 3987 return true; 3988} 3989 3990} // namespace 3991 3992Node* JSCallReducer::CheckArrayLength(Node* array, ElementsKind elements_kind, 3993 uint32_t array_length, 3994 const FeedbackSource& feedback_source, 3995 Effect effect, Control control) { 3996 Node* length = effect = graph()->NewNode( 3997 simplified()->LoadField(AccessBuilder::ForJSArrayLength(elements_kind)), 3998 array, effect, control); 3999 Node* check = graph()->NewNode(simplified()->NumberEqual(), length, 4000 jsgraph()->Constant(array_length)); 4001 return graph()->NewNode( 4002 simplified()->CheckIf(DeoptimizeReason::kArrayLengthChanged, 4003 feedback_source), 4004 check, effect, control); 4005} 4006 4007Reduction 4008JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpreadOfCreateArguments( 4009 Node* node, Node* arguments_list, int arraylike_or_spread_index, 4010 CallFrequency const& frequency, FeedbackSource const& feedback, 4011 SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) { 4012 DCHECK_EQ(arguments_list->opcode(), IrOpcode::kJSCreateArguments); 4013 4014 // Check if {node} is the only value user of {arguments_list} (except for 4015 // value uses in frame states). If not, we give up for now. 4016 for (Edge edge : arguments_list->use_edges()) { 4017 if (!NodeProperties::IsValueEdge(edge)) continue; 4018 Node* const user = edge.from(); 4019 switch (user->opcode()) { 4020 case IrOpcode::kCheckMaps: 4021 case IrOpcode::kFrameState: 4022 case IrOpcode::kStateValues: 4023 case IrOpcode::kReferenceEqual: 4024 case IrOpcode::kReturn: 4025 // Ignore safe uses that definitely don't mess with the arguments. 4026 continue; 4027 case IrOpcode::kLoadField: { 4028 DCHECK_EQ(arguments_list, user->InputAt(0)); 4029 FieldAccess const& access = FieldAccessOf(user->op()); 4030 if (access.offset == JSArray::kLengthOffset) { 4031 // Ignore uses for arguments#length. 4032 STATIC_ASSERT( 4033 static_cast<int>(JSArray::kLengthOffset) == 4034 static_cast<int>(JSStrictArgumentsObject::kLengthOffset)); 4035 STATIC_ASSERT( 4036 static_cast<int>(JSArray::kLengthOffset) == 4037 static_cast<int>(JSSloppyArgumentsObject::kLengthOffset)); 4038 continue; 4039 } else if (access.offset == JSObject::kElementsOffset) { 4040 // Ignore safe uses for arguments#elements. 4041 if (IsSafeArgumentsElements(user)) continue; 4042 } 4043 break; 4044 } 4045 case IrOpcode::kJSCallWithArrayLike: { 4046 // Ignore uses as argumentsList input to calls with array like. 4047 JSCallWithArrayLikeNode n(user); 4048 if (n.Argument(0) == arguments_list) continue; 4049 break; 4050 } 4051 case IrOpcode::kJSConstructWithArrayLike: { 4052 // Ignore uses as argumentsList input to calls with array like. 4053 JSConstructWithArrayLikeNode n(user); 4054 if (n.Argument(0) == arguments_list) continue; 4055 break; 4056 } 4057 case IrOpcode::kJSCallWithSpread: { 4058 // Ignore uses as spread input to calls with spread. 4059 JSCallWithSpreadNode n(user); 4060 if (n.LastArgument() == arguments_list) continue; 4061 break; 4062 } 4063 case IrOpcode::kJSConstructWithSpread: { 4064 // Ignore uses as spread input to construct with spread. 4065 JSConstructWithSpreadNode n(user); 4066 if (n.LastArgument() == arguments_list) continue; 4067 break; 4068 } 4069 default: 4070 break; 4071 } 4072 // We cannot currently reduce the {node} to something better than what 4073 // it already is, but we might be able to do something about the {node} 4074 // later, so put it on the waitlist and try again during finalization. 4075 waitlist_.insert(node); 4076 return NoChange(); 4077 } 4078 4079 // Get to the actual frame state from which to extract the arguments; 4080 // we can only optimize this in case the {node} was already inlined into 4081 // some other function (and same for the {arguments_list}). 4082 CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op()); 4083 FrameState frame_state = 4084 FrameState{NodeProperties::GetFrameStateInput(arguments_list)}; 4085 4086 int formal_parameter_count; 4087 { 4088 Handle<SharedFunctionInfo> shared; 4089 if (!frame_state.frame_state_info().shared_info().ToHandle(&shared)) { 4090 return NoChange(); 4091 } 4092 formal_parameter_count = 4093 MakeRef(broker(), shared) 4094 .internal_formal_parameter_count_without_receiver(); 4095 } 4096 4097 if (type == CreateArgumentsType::kMappedArguments) { 4098 // Mapped arguments (sloppy mode) that are aliased can only be handled 4099 // here if there's no side-effect between the {node} and the {arg_array}. 4100 // TODO(turbofan): Further relax this constraint. 4101 if (formal_parameter_count != 0) { 4102 Node* effect = NodeProperties::GetEffectInput(node); 4103 if (!NodeProperties::NoObservableSideEffectBetween(effect, 4104 arguments_list)) { 4105 return NoChange(); 4106 } 4107 } 4108 } 4109 4110 // For call/construct with spread, we need to also install a code 4111 // dependency on the array iterator lookup protector cell to ensure 4112 // that no one messed with the %ArrayIteratorPrototype%.next method. 4113 if (IsCallOrConstructWithSpread(node)) { 4114 if (!dependencies()->DependOnArrayIteratorProtector()) return NoChange(); 4115 } 4116 4117 // Remove the {arguments_list} input from the {node}. 4118 node->RemoveInput(arraylike_or_spread_index); 4119 4120 // The index of the first relevant parameter. Only non-zero when looking at 4121 // rest parameters, in which case it is set to the index of the first rest 4122 // parameter. 4123 const int start_index = (type == CreateArgumentsType::kRestParameter) 4124 ? formal_parameter_count 4125 : 0; 4126 4127 // After removing the arraylike or spread object, the argument count is: 4128 int argc = 4129 arraylike_or_spread_index - JSCallOrConstructNode::FirstArgumentIndex(); 4130 // Check if are spreading to inlined arguments or to the arguments of 4131 // the outermost function. 4132 if (frame_state.outer_frame_state()->opcode() != IrOpcode::kFrameState) { 4133 Operator const* op; 4134 if (IsCallWithArrayLikeOrSpread(node)) { 4135 static constexpr int kTargetAndReceiver = 2; 4136 op = javascript()->CallForwardVarargs(argc + kTargetAndReceiver, 4137 start_index); 4138 } else { 4139 static constexpr int kTargetAndNewTarget = 2; 4140 op = javascript()->ConstructForwardVarargs(argc + kTargetAndNewTarget, 4141 start_index); 4142 } 4143 node->RemoveInput(JSCallOrConstructNode::FeedbackVectorIndexForArgc(argc)); 4144 NodeProperties::ChangeOp(node, op); 4145 return Changed(node); 4146 } 4147 // Get to the actual frame state from which to extract the arguments; 4148 // we can only optimize this in case the {node} was already inlined into 4149 // some other function (and same for the {arg_array}). 4150 FrameState outer_state{frame_state.outer_frame_state()}; 4151 FrameStateInfo outer_info = outer_state.frame_state_info(); 4152 if (outer_info.type() == FrameStateType::kArgumentsAdaptor) { 4153 // Need to take the parameters from the arguments adaptor. 4154 frame_state = outer_state; 4155 } 4156 // Add the actual parameters to the {node}, skipping the receiver. 4157 StateValuesAccess parameters_access(frame_state.parameters()); 4158 for (auto it = parameters_access.begin_without_receiver_and_skip(start_index); 4159 !it.done(); ++it) { 4160 DCHECK_NOT_NULL(it.node()); 4161 node->InsertInput(graph()->zone(), 4162 JSCallOrConstructNode::ArgumentIndex(argc++), it.node()); 4163 } 4164 4165 if (IsCallWithArrayLikeOrSpread(node)) { 4166 NodeProperties::ChangeOp( 4167 node, javascript()->Call(JSCallNode::ArityForArgc(argc), frequency, 4168 feedback, ConvertReceiverMode::kAny, 4169 speculation_mode, feedback_relation)); 4170 return Changed(node).FollowedBy(ReduceJSCall(node)); 4171 } else { 4172 NodeProperties::ChangeOp( 4173 node, javascript()->Construct(JSConstructNode::ArityForArgc(argc), 4174 frequency, feedback)); 4175 4176 // Check whether the given new target value is a constructor function. The 4177 // replacement {JSConstruct} operator only checks the passed target value 4178 // but relies on the new target value to be implicitly valid. 4179 CheckIfConstructor(node); 4180 return Changed(node).FollowedBy(ReduceJSConstruct(node)); 4181 } 4182} 4183 4184Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( 4185 Node* node, int argument_count, int arraylike_or_spread_index, 4186 CallFrequency const& frequency, FeedbackSource const& feedback_source, 4187 SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation, 4188 Node* target, Effect effect, Control control) { 4189 DCHECK(IsCallOrConstructWithArrayLike(node) || 4190 IsCallOrConstructWithSpread(node)); 4191 DCHECK_IMPLIES(speculation_mode == SpeculationMode::kAllowSpeculation, 4192 feedback_source.IsValid()); 4193 4194 Node* arguments_list = 4195 NodeProperties::GetValueInput(node, arraylike_or_spread_index); 4196 4197 if (arguments_list->opcode() == IrOpcode::kJSCreateArguments) { 4198 return ReduceCallOrConstructWithArrayLikeOrSpreadOfCreateArguments( 4199 node, arguments_list, arraylike_or_spread_index, frequency, 4200 feedback_source, speculation_mode, feedback_relation); 4201 } 4202 4203 if (!FLAG_turbo_optimize_apply) return NoChange(); 4204 4205 // Optimization of construct nodes not supported yet. 4206 if (!IsCallWithArrayLikeOrSpread(node)) return NoChange(); 4207 4208 // Avoid deoptimization loops. 4209 if (speculation_mode != SpeculationMode::kAllowSpeculation) return NoChange(); 4210 4211 // Only optimize with array literals. 4212 if (arguments_list->opcode() != IrOpcode::kJSCreateLiteralArray && 4213 arguments_list->opcode() != IrOpcode::kJSCreateEmptyLiteralArray) { 4214 return NoChange(); 4215 } 4216 4217 // For call/construct with spread, we need to also install a code 4218 // dependency on the array iterator lookup protector cell to ensure 4219 // that no one messed with the %ArrayIteratorPrototype%.next method. 4220 if (IsCallOrConstructWithSpread(node)) { 4221 if (!dependencies()->DependOnArrayIteratorProtector()) return NoChange(); 4222 } 4223 4224 if (arguments_list->opcode() == IrOpcode::kJSCreateEmptyLiteralArray) { 4225 if (generated_calls_with_array_like_or_spread_.count(node)) { 4226 return NoChange(); // Avoid infinite recursion. 4227 } 4228 JSCallReducerAssembler a(this, node); 4229 Node* subgraph = a.ReduceJSCallWithArrayLikeOrSpreadOfEmpty( 4230 &generated_calls_with_array_like_or_spread_); 4231 return ReplaceWithSubgraph(&a, subgraph); 4232 } 4233 4234 DCHECK_EQ(arguments_list->opcode(), IrOpcode::kJSCreateLiteralArray); 4235 int new_argument_count; 4236 4237 // Find array length and elements' kind from the feedback's allocation 4238 // site's boilerplate JSArray. 4239 JSCreateLiteralOpNode args_node(arguments_list); 4240 CreateLiteralParameters const& args_params = args_node.Parameters(); 4241 const FeedbackSource& array_feedback = args_params.feedback(); 4242 const ProcessedFeedback& feedback = 4243 broker()->GetFeedbackForArrayOrObjectLiteral(array_feedback); 4244 if (feedback.IsInsufficient()) return NoChange(); 4245 4246 AllocationSiteRef site = feedback.AsLiteral().value(); 4247 if (!site.boilerplate().has_value()) return NoChange(); 4248 4249 JSArrayRef boilerplate_array = site.boilerplate()->AsJSArray(); 4250 int const array_length = boilerplate_array.GetBoilerplateLength().AsSmi(); 4251 4252 // We'll replace the arguments_list input with {array_length} element loads. 4253 new_argument_count = argument_count - 1 + array_length; 4254 4255 // Do not optimize calls with a large number of arguments. 4256 // Arbitrarily sets the limit to 32 arguments. 4257 const int kMaxArityForOptimizedFunctionApply = 32; 4258 if (new_argument_count > kMaxArityForOptimizedFunctionApply) { 4259 return NoChange(); 4260 } 4261 4262 // Determine the array's map. 4263 MapRef array_map = boilerplate_array.map(); 4264 if (!array_map.supports_fast_array_iteration()) { 4265 return NoChange(); 4266 } 4267 4268 // Check and depend on NoElementsProtector. 4269 if (!dependencies()->DependOnNoElementsProtector()) { 4270 return NoChange(); 4271 } 4272 4273 // Remove the {arguments_list} node which will be replaced by a sequence of 4274 // LoadElement nodes. 4275 node->RemoveInput(arraylike_or_spread_index); 4276 4277 // Speculate on that array's map is still equal to the dynamic map of 4278 // arguments_list; generate a map check. 4279 effect = graph()->NewNode( 4280 simplified()->CheckMaps(CheckMapsFlag::kNone, 4281 ZoneHandleSet<Map>(array_map.object()), 4282 feedback_source), 4283 arguments_list, effect, control); 4284 4285 // Speculate on that array's length being equal to the dynamic length of 4286 // arguments_list; generate a deopt check. 4287 ElementsKind elements_kind = array_map.elements_kind(); 4288 effect = CheckArrayLength(arguments_list, elements_kind, array_length, 4289 feedback_source, effect, control); 4290 4291 // Generate N element loads to replace the {arguments_list} node with a set 4292 // of arguments loaded from it. 4293 Node* elements = effect = graph()->NewNode( 4294 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), 4295 arguments_list, effect, control); 4296 for (int i = 0; i < array_length; i++) { 4297 // Load the i-th element from the array. 4298 Node* index = jsgraph()->Constant(i); 4299 Node* load = effect = graph()->NewNode( 4300 simplified()->LoadElement( 4301 AccessBuilder::ForFixedArrayElement(elements_kind)), 4302 elements, index, effect, control); 4303 4304 // In "holey" arrays some arguments might be missing and we pass 4305 // 'undefined' instead. 4306 if (IsHoleyElementsKind(elements_kind)) { 4307 if (elements_kind == HOLEY_DOUBLE_ELEMENTS) { 4308 // May deopt for holey double elements. 4309 load = effect = graph()->NewNode( 4310 simplified()->CheckFloat64Hole( 4311 CheckFloat64HoleMode::kAllowReturnHole, feedback_source), 4312 load, effect, control); 4313 } else { 4314 load = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), 4315 load); 4316 } 4317 } 4318 4319 node->InsertInput(graph()->zone(), arraylike_or_spread_index + i, load); 4320 } 4321 4322 NodeProperties::ChangeOp( 4323 node, 4324 javascript()->Call(JSCallNode::ArityForArgc(new_argument_count), 4325 frequency, feedback_source, ConvertReceiverMode::kAny, 4326 speculation_mode, CallFeedbackRelation::kUnrelated)); 4327 NodeProperties::ReplaceEffectInput(node, effect); 4328 return Changed(node).FollowedBy(ReduceJSCall(node)); 4329} 4330 4331bool JSCallReducer::IsBuiltinOrApiFunction(JSFunctionRef function) const { 4332 // TODO(neis): Add a way to check if function template info isn't serialized 4333 // and add a warning in such cases. Currently we can't tell if function 4334 // template info doesn't exist or wasn't serialized. 4335 return function.shared().HasBuiltinId() || 4336 function.shared().function_template_info().has_value(); 4337} 4338 4339Reduction JSCallReducer::ReduceJSCall(Node* node) { 4340 if (broker()->StackHasOverflowed()) return NoChange(); 4341 4342 JSCallNode n(node); 4343 CallParameters const& p = n.Parameters(); 4344 Node* target = n.target(); 4345 Effect effect = n.effect(); 4346 Control control = n.control(); 4347 int arity = p.arity_without_implicit_args(); 4348 4349 // Try to specialize JSCall {node}s with constant {target}s. 4350 HeapObjectMatcher m(target); 4351 if (m.HasResolvedValue()) { 4352 ObjectRef target_ref = m.Ref(broker()); 4353 if (target_ref.IsJSFunction()) { 4354 JSFunctionRef function = target_ref.AsJSFunction(); 4355 4356 // Don't inline cross native context. 4357 if (!function.native_context().equals(native_context())) { 4358 return NoChange(); 4359 } 4360 4361 return ReduceJSCall(node, function.shared()); 4362 } else if (target_ref.IsJSBoundFunction()) { 4363 JSBoundFunctionRef function = target_ref.AsJSBoundFunction(); 4364 ObjectRef bound_this = function.bound_this(); 4365 ConvertReceiverMode const convert_mode = 4366 bound_this.IsNullOrUndefined() 4367 ? ConvertReceiverMode::kNullOrUndefined 4368 : ConvertReceiverMode::kNotNullOrUndefined; 4369 4370 // TODO(jgruber): Inline this block below once TryGet is guaranteed to 4371 // succeed. 4372 FixedArrayRef bound_arguments = function.bound_arguments(); 4373 const int bound_arguments_length = bound_arguments.length(); 4374 static constexpr int kInlineSize = 16; // Arbitrary. 4375 base::SmallVector<Node*, kInlineSize> args; 4376 for (int i = 0; i < bound_arguments_length; ++i) { 4377 base::Optional<ObjectRef> maybe_arg = bound_arguments.TryGet(i); 4378 if (!maybe_arg.has_value()) { 4379 TRACE_BROKER_MISSING(broker(), "bound argument"); 4380 return NoChange(); 4381 } 4382 args.emplace_back(jsgraph()->Constant(maybe_arg.value())); 4383 } 4384 4385 // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]]. 4386 NodeProperties::ReplaceValueInput( 4387 node, jsgraph()->Constant(function.bound_target_function()), 4388 JSCallNode::TargetIndex()); 4389 NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this), 4390 JSCallNode::ReceiverIndex()); 4391 4392 // Insert the [[BoundArguments]] for {node}. 4393 for (int i = 0; i < bound_arguments_length; ++i) { 4394 node->InsertInput(graph()->zone(), i + 2, args[i]); 4395 arity++; 4396 } 4397 4398 NodeProperties::ChangeOp( 4399 node, 4400 javascript()->Call(JSCallNode::ArityForArgc(arity), p.frequency(), 4401 p.feedback(), convert_mode, p.speculation_mode(), 4402 CallFeedbackRelation::kUnrelated)); 4403 4404 // Try to further reduce the JSCall {node}. 4405 return Changed(node).FollowedBy(ReduceJSCall(node)); 4406 } 4407 4408 // Don't mess with other {node}s that have a constant {target}. 4409 // TODO(bmeurer): Also support proxies here. 4410 return NoChange(); 4411 } 4412 4413 // If {target} is the result of a JSCreateClosure operation, we can 4414 // just immediately try to inline based on the SharedFunctionInfo, 4415 // since TurboFan generally doesn't inline cross-context, and hence 4416 // the {target} must have the same native context as the call site. 4417 // Same if the {target} is the result of a CheckClosure operation. 4418 if (target->opcode() == IrOpcode::kJSCreateClosure) { 4419 CreateClosureParameters const& params = 4420 JSCreateClosureNode{target}.Parameters(); 4421 return ReduceJSCall(node, params.shared_info(broker())); 4422 } else if (target->opcode() == IrOpcode::kCheckClosure) { 4423 FeedbackCellRef cell = MakeRef(broker(), FeedbackCellOf(target->op())); 4424 base::Optional<SharedFunctionInfoRef> shared = cell.shared_function_info(); 4425 if (!shared.has_value()) { 4426 TRACE_BROKER_MISSING(broker(), "Unable to reduce JSCall. FeedbackCell " 4427 << cell << " has no FeedbackVector"); 4428 return NoChange(); 4429 } 4430 return ReduceJSCall(node, *shared); 4431 } 4432 4433 // If {target} is the result of a JSCreateBoundFunction operation, 4434 // we can just fold the construction and call the bound target 4435 // function directly instead. 4436 if (target->opcode() == IrOpcode::kJSCreateBoundFunction) { 4437 Node* bound_target_function = NodeProperties::GetValueInput(target, 0); 4438 Node* bound_this = NodeProperties::GetValueInput(target, 1); 4439 int const bound_arguments_length = 4440 static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity()); 4441 4442 // Patch the {node} to use [[BoundTargetFunction]] and [[BoundThis]]. 4443 NodeProperties::ReplaceValueInput(node, bound_target_function, 4444 n.TargetIndex()); 4445 NodeProperties::ReplaceValueInput(node, bound_this, n.ReceiverIndex()); 4446 4447 // Insert the [[BoundArguments]] for {node}. 4448 for (int i = 0; i < bound_arguments_length; ++i) { 4449 Node* value = NodeProperties::GetValueInput(target, 2 + i); 4450 node->InsertInput(graph()->zone(), n.ArgumentIndex(i), value); 4451 arity++; 4452 } 4453 4454 // Update the JSCall operator on {node}. 4455 ConvertReceiverMode const convert_mode = 4456 NodeProperties::CanBeNullOrUndefined(broker(), bound_this, effect) 4457 ? ConvertReceiverMode::kAny 4458 : ConvertReceiverMode::kNotNullOrUndefined; 4459 NodeProperties::ChangeOp( 4460 node, 4461 javascript()->Call(JSCallNode::ArityForArgc(arity), p.frequency(), 4462 p.feedback(), convert_mode, p.speculation_mode(), 4463 CallFeedbackRelation::kUnrelated)); 4464 4465 // Try to further reduce the JSCall {node}. 4466 return Changed(node).FollowedBy(ReduceJSCall(node)); 4467 } 4468 4469 if (!ShouldUseCallICFeedback(target) || 4470 p.feedback_relation() == CallFeedbackRelation::kUnrelated || 4471 !p.feedback().IsValid()) { 4472 return NoChange(); 4473 } 4474 4475 ProcessedFeedback const& feedback = 4476 broker()->GetFeedbackForCall(p.feedback()); 4477 if (feedback.IsInsufficient()) { 4478 return ReduceForInsufficientFeedback( 4479 node, DeoptimizeReason::kInsufficientTypeFeedbackForCall); 4480 } 4481 4482 base::Optional<HeapObjectRef> feedback_target; 4483 if (p.feedback_relation() == CallFeedbackRelation::kTarget) { 4484 feedback_target = feedback.AsCall().target(); 4485 } else { 4486 DCHECK_EQ(p.feedback_relation(), CallFeedbackRelation::kReceiver); 4487 feedback_target = native_context().function_prototype_apply(); 4488 } 4489 4490 if (feedback_target.has_value() && feedback_target->map().is_callable()) { 4491 Node* target_function = jsgraph()->Constant(*feedback_target); 4492 4493 // Check that the {target} is still the {target_function}. 4494 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, 4495 target_function); 4496 effect = graph()->NewNode( 4497 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, 4498 effect, control); 4499 4500 // Specialize the JSCall node to the {target_function}. 4501 NodeProperties::ReplaceValueInput(node, target_function, n.TargetIndex()); 4502 NodeProperties::ReplaceEffectInput(node, effect); 4503 4504 // Try to further reduce the JSCall {node}. 4505 return Changed(node).FollowedBy(ReduceJSCall(node)); 4506 } else if (feedback_target.has_value() && feedback_target->IsFeedbackCell()) { 4507 FeedbackCellRef feedback_cell = feedback_target.value().AsFeedbackCell(); 4508 // TODO(neis): This check seems unnecessary. 4509 if (feedback_cell.feedback_vector().has_value()) { 4510 // Check that {target} is a closure with given {feedback_cell}, 4511 // which uniquely identifies a given function inside a native context. 4512 Node* target_closure = effect = 4513 graph()->NewNode(simplified()->CheckClosure(feedback_cell.object()), 4514 target, effect, control); 4515 4516 // Specialize the JSCall node to the {target_closure}. 4517 NodeProperties::ReplaceValueInput(node, target_closure, n.TargetIndex()); 4518 NodeProperties::ReplaceEffectInput(node, effect); 4519 4520 // Try to further reduce the JSCall {node}. 4521 return Changed(node).FollowedBy(ReduceJSCall(node)); 4522 } 4523 } 4524 return NoChange(); 4525} 4526 4527Reduction JSCallReducer::ReduceJSCall(Node* node, 4528 const SharedFunctionInfoRef& shared) { 4529 JSCallNode n(node); 4530 Node* target = n.target(); 4531 4532 // Do not reduce calls to functions with break points. 4533 // If this state changes during background compilation, the compilation 4534 // job will be aborted from the main thread (see 4535 // Debug::PrepareFunctionForDebugExecution()). 4536 if (shared.HasBreakInfo()) return NoChange(); 4537 4538 // Class constructors are callable, but [[Call]] will raise an exception. 4539 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). 4540 if (IsClassConstructor(shared.kind())) { 4541 NodeProperties::ReplaceValueInputs(node, target); 4542 NodeProperties::ChangeOp( 4543 node, javascript()->CallRuntime( 4544 Runtime::kThrowConstructorNonCallableError, 1)); 4545 return Changed(node); 4546 } 4547 4548 // Check for known builtin functions. 4549 4550 Builtin builtin = 4551 shared.HasBuiltinId() ? shared.builtin_id() : Builtin::kNoBuiltinId; 4552 switch (builtin) { 4553 case Builtin::kArrayConstructor: 4554 return ReduceArrayConstructor(node); 4555 case Builtin::kBooleanConstructor: 4556 return ReduceBooleanConstructor(node); 4557 case Builtin::kFunctionPrototypeApply: 4558 return ReduceFunctionPrototypeApply(node); 4559 case Builtin::kFastFunctionPrototypeBind: 4560 return ReduceFunctionPrototypeBind(node); 4561 case Builtin::kFunctionPrototypeCall: 4562 return ReduceFunctionPrototypeCall(node); 4563 case Builtin::kFunctionPrototypeHasInstance: 4564 return ReduceFunctionPrototypeHasInstance(node); 4565 case Builtin::kObjectConstructor: 4566 return ReduceObjectConstructor(node); 4567 case Builtin::kObjectCreate: 4568 return ReduceObjectCreate(node); 4569 case Builtin::kObjectGetPrototypeOf: 4570 return ReduceObjectGetPrototypeOf(node); 4571 case Builtin::kObjectIs: 4572 return ReduceObjectIs(node); 4573 case Builtin::kObjectPrototypeGetProto: 4574 return ReduceObjectPrototypeGetProto(node); 4575 case Builtin::kObjectPrototypeHasOwnProperty: 4576 return ReduceObjectPrototypeHasOwnProperty(node); 4577 case Builtin::kObjectPrototypeIsPrototypeOf: 4578 return ReduceObjectPrototypeIsPrototypeOf(node); 4579 case Builtin::kReflectApply: 4580 return ReduceReflectApply(node); 4581 case Builtin::kReflectConstruct: 4582 return ReduceReflectConstruct(node); 4583 case Builtin::kReflectGet: 4584 return ReduceReflectGet(node); 4585 case Builtin::kReflectGetPrototypeOf: 4586 return ReduceReflectGetPrototypeOf(node); 4587 case Builtin::kReflectHas: 4588 return ReduceReflectHas(node); 4589 case Builtin::kArrayForEach: 4590 return ReduceArrayForEach(node, shared); 4591 case Builtin::kArrayMap: 4592 return ReduceArrayMap(node, shared); 4593 case Builtin::kArrayFilter: 4594 return ReduceArrayFilter(node, shared); 4595 case Builtin::kArrayReduce: 4596 return ReduceArrayReduce(node, shared); 4597 case Builtin::kArrayReduceRight: 4598 return ReduceArrayReduceRight(node, shared); 4599 case Builtin::kArrayPrototypeFind: 4600 return ReduceArrayFind(node, shared); 4601 case Builtin::kArrayPrototypeFindIndex: 4602 return ReduceArrayFindIndex(node, shared); 4603 case Builtin::kArrayEvery: 4604 return ReduceArrayEvery(node, shared); 4605 case Builtin::kArrayIndexOf: 4606 return ReduceArrayIndexOf(node); 4607 case Builtin::kArrayIncludes: 4608 return ReduceArrayIncludes(node); 4609 case Builtin::kArraySome: 4610 return ReduceArraySome(node, shared); 4611 case Builtin::kArrayPrototypePush: 4612 return ReduceArrayPrototypePush(node); 4613 case Builtin::kArrayPrototypePop: 4614 return ReduceArrayPrototypePop(node); 4615 case Builtin::kArrayPrototypeShift: 4616 return ReduceArrayPrototypeShift(node); 4617 case Builtin::kArrayPrototypeSlice: 4618 return ReduceArrayPrototypeSlice(node); 4619 case Builtin::kArrayPrototypeEntries: 4620 return ReduceArrayIterator(node, ArrayIteratorKind::kArrayLike, 4621 IterationKind::kEntries); 4622 case Builtin::kArrayPrototypeKeys: 4623 return ReduceArrayIterator(node, ArrayIteratorKind::kArrayLike, 4624 IterationKind::kKeys); 4625 case Builtin::kArrayPrototypeValues: 4626 return ReduceArrayIterator(node, ArrayIteratorKind::kArrayLike, 4627 IterationKind::kValues); 4628 case Builtin::kArrayIteratorPrototypeNext: 4629 return ReduceArrayIteratorPrototypeNext(node); 4630 case Builtin::kArrayIsArray: 4631 return ReduceArrayIsArray(node); 4632 case Builtin::kArrayBufferIsView: 4633 return ReduceArrayBufferIsView(node); 4634 case Builtin::kDataViewPrototypeGetByteLength: 4635 return ReduceArrayBufferViewAccessor( 4636 node, JS_DATA_VIEW_TYPE, 4637 AccessBuilder::ForJSArrayBufferViewByteLength()); 4638 case Builtin::kDataViewPrototypeGetByteOffset: 4639 return ReduceArrayBufferViewAccessor( 4640 node, JS_DATA_VIEW_TYPE, 4641 AccessBuilder::ForJSArrayBufferViewByteOffset()); 4642 case Builtin::kDataViewPrototypeGetUint8: 4643 return ReduceDataViewAccess(node, DataViewAccess::kGet, 4644 ExternalArrayType::kExternalUint8Array); 4645 case Builtin::kDataViewPrototypeGetInt8: 4646 return ReduceDataViewAccess(node, DataViewAccess::kGet, 4647 ExternalArrayType::kExternalInt8Array); 4648 case Builtin::kDataViewPrototypeGetUint16: 4649 return ReduceDataViewAccess(node, DataViewAccess::kGet, 4650 ExternalArrayType::kExternalUint16Array); 4651 case Builtin::kDataViewPrototypeGetInt16: 4652 return ReduceDataViewAccess(node, DataViewAccess::kGet, 4653 ExternalArrayType::kExternalInt16Array); 4654 case Builtin::kDataViewPrototypeGetUint32: 4655 return ReduceDataViewAccess(node, DataViewAccess::kGet, 4656 ExternalArrayType::kExternalUint32Array); 4657 case Builtin::kDataViewPrototypeGetInt32: 4658 return ReduceDataViewAccess(node, DataViewAccess::kGet, 4659 ExternalArrayType::kExternalInt32Array); 4660 case Builtin::kDataViewPrototypeGetFloat32: 4661 return ReduceDataViewAccess(node, DataViewAccess::kGet, 4662 ExternalArrayType::kExternalFloat32Array); 4663 case Builtin::kDataViewPrototypeGetFloat64: 4664 return ReduceDataViewAccess(node, DataViewAccess::kGet, 4665 ExternalArrayType::kExternalFloat64Array); 4666 case Builtin::kDataViewPrototypeSetUint8: 4667 return ReduceDataViewAccess(node, DataViewAccess::kSet, 4668 ExternalArrayType::kExternalUint8Array); 4669 case Builtin::kDataViewPrototypeSetInt8: 4670 return ReduceDataViewAccess(node, DataViewAccess::kSet, 4671 ExternalArrayType::kExternalInt8Array); 4672 case Builtin::kDataViewPrototypeSetUint16: 4673 return ReduceDataViewAccess(node, DataViewAccess::kSet, 4674 ExternalArrayType::kExternalUint16Array); 4675 case Builtin::kDataViewPrototypeSetInt16: 4676 return ReduceDataViewAccess(node, DataViewAccess::kSet, 4677 ExternalArrayType::kExternalInt16Array); 4678 case Builtin::kDataViewPrototypeSetUint32: 4679 return ReduceDataViewAccess(node, DataViewAccess::kSet, 4680 ExternalArrayType::kExternalUint32Array); 4681 case Builtin::kDataViewPrototypeSetInt32: 4682 return ReduceDataViewAccess(node, DataViewAccess::kSet, 4683 ExternalArrayType::kExternalInt32Array); 4684 case Builtin::kDataViewPrototypeSetFloat32: 4685 return ReduceDataViewAccess(node, DataViewAccess::kSet, 4686 ExternalArrayType::kExternalFloat32Array); 4687 case Builtin::kDataViewPrototypeSetFloat64: 4688 return ReduceDataViewAccess(node, DataViewAccess::kSet, 4689 ExternalArrayType::kExternalFloat64Array); 4690 case Builtin::kTypedArrayPrototypeByteLength: 4691 return ReduceArrayBufferViewAccessor( 4692 node, JS_TYPED_ARRAY_TYPE, 4693 AccessBuilder::ForJSArrayBufferViewByteLength()); 4694 case Builtin::kTypedArrayPrototypeByteOffset: 4695 return ReduceArrayBufferViewAccessor( 4696 node, JS_TYPED_ARRAY_TYPE, 4697 AccessBuilder::ForJSArrayBufferViewByteOffset()); 4698 case Builtin::kTypedArrayPrototypeLength: 4699 return ReduceArrayBufferViewAccessor( 4700 node, JS_TYPED_ARRAY_TYPE, AccessBuilder::ForJSTypedArrayLength()); 4701 case Builtin::kTypedArrayPrototypeToStringTag: 4702 return ReduceTypedArrayPrototypeToStringTag(node); 4703 case Builtin::kMathAbs: 4704 return ReduceMathUnary(node, simplified()->NumberAbs()); 4705 case Builtin::kMathAcos: 4706 return ReduceMathUnary(node, simplified()->NumberAcos()); 4707 case Builtin::kMathAcosh: 4708 return ReduceMathUnary(node, simplified()->NumberAcosh()); 4709 case Builtin::kMathAsin: 4710 return ReduceMathUnary(node, simplified()->NumberAsin()); 4711 case Builtin::kMathAsinh: 4712 return ReduceMathUnary(node, simplified()->NumberAsinh()); 4713 case Builtin::kMathAtan: 4714 return ReduceMathUnary(node, simplified()->NumberAtan()); 4715 case Builtin::kMathAtanh: 4716 return ReduceMathUnary(node, simplified()->NumberAtanh()); 4717 case Builtin::kMathCbrt: 4718 return ReduceMathUnary(node, simplified()->NumberCbrt()); 4719 case Builtin::kMathCeil: 4720 return ReduceMathUnary(node, simplified()->NumberCeil()); 4721 case Builtin::kMathCos: 4722 return ReduceMathUnary(node, simplified()->NumberCos()); 4723 case Builtin::kMathCosh: 4724 return ReduceMathUnary(node, simplified()->NumberCosh()); 4725 case Builtin::kMathExp: 4726 return ReduceMathUnary(node, simplified()->NumberExp()); 4727 case Builtin::kMathExpm1: 4728 return ReduceMathUnary(node, simplified()->NumberExpm1()); 4729 case Builtin::kMathFloor: 4730 return ReduceMathUnary(node, simplified()->NumberFloor()); 4731 case Builtin::kMathFround: 4732 return ReduceMathUnary(node, simplified()->NumberFround()); 4733 case Builtin::kMathLog: 4734 return ReduceMathUnary(node, simplified()->NumberLog()); 4735 case Builtin::kMathLog1p: 4736 return ReduceMathUnary(node, simplified()->NumberLog1p()); 4737 case Builtin::kMathLog10: 4738 return ReduceMathUnary(node, simplified()->NumberLog10()); 4739 case Builtin::kMathLog2: 4740 return ReduceMathUnary(node, simplified()->NumberLog2()); 4741 case Builtin::kMathRound: 4742 return ReduceMathUnary(node, simplified()->NumberRound()); 4743 case Builtin::kMathSign: 4744 return ReduceMathUnary(node, simplified()->NumberSign()); 4745 case Builtin::kMathSin: 4746 return ReduceMathUnary(node, simplified()->NumberSin()); 4747 case Builtin::kMathSinh: 4748 return ReduceMathUnary(node, simplified()->NumberSinh()); 4749 case Builtin::kMathSqrt: 4750 return ReduceMathUnary(node, simplified()->NumberSqrt()); 4751 case Builtin::kMathTan: 4752 return ReduceMathUnary(node, simplified()->NumberTan()); 4753 case Builtin::kMathTanh: 4754 return ReduceMathUnary(node, simplified()->NumberTanh()); 4755 case Builtin::kMathTrunc: 4756 return ReduceMathUnary(node, simplified()->NumberTrunc()); 4757 case Builtin::kMathAtan2: 4758 return ReduceMathBinary(node, simplified()->NumberAtan2()); 4759 case Builtin::kMathPow: 4760 return ReduceMathBinary(node, simplified()->NumberPow()); 4761 case Builtin::kMathClz32: 4762 return ReduceMathClz32(node); 4763 case Builtin::kMathImul: 4764 return ReduceMathImul(node); 4765 case Builtin::kMathMax: 4766 return ReduceMathMinMax(node, simplified()->NumberMax(), 4767 jsgraph()->Constant(-V8_INFINITY)); 4768 case Builtin::kMathMin: 4769 return ReduceMathMinMax(node, simplified()->NumberMin(), 4770 jsgraph()->Constant(V8_INFINITY)); 4771 case Builtin::kNumberIsFinite: 4772 return ReduceNumberIsFinite(node); 4773 case Builtin::kNumberIsInteger: 4774 return ReduceNumberIsInteger(node); 4775 case Builtin::kNumberIsSafeInteger: 4776 return ReduceNumberIsSafeInteger(node); 4777 case Builtin::kNumberIsNaN: 4778 return ReduceNumberIsNaN(node); 4779 case Builtin::kNumberParseInt: 4780 return ReduceNumberParseInt(node); 4781 case Builtin::kGlobalIsFinite: 4782 return ReduceGlobalIsFinite(node); 4783 case Builtin::kGlobalIsNaN: 4784 return ReduceGlobalIsNaN(node); 4785 case Builtin::kMapPrototypeGet: 4786 return ReduceMapPrototypeGet(node); 4787 case Builtin::kMapPrototypeHas: 4788 return ReduceMapPrototypeHas(node); 4789 case Builtin::kRegExpPrototypeTest: 4790 return ReduceRegExpPrototypeTest(node); 4791 case Builtin::kReturnReceiver: 4792 return ReduceReturnReceiver(node); 4793 case Builtin::kStringPrototypeIndexOf: 4794 return ReduceStringPrototypeIndexOfIncludes( 4795 node, StringIndexOfIncludesVariant::kIndexOf); 4796 case Builtin::kStringPrototypeIncludes: 4797 return ReduceStringPrototypeIndexOfIncludes( 4798 node, StringIndexOfIncludesVariant::kIncludes); 4799 case Builtin::kStringPrototypeCharAt: 4800 return ReduceStringPrototypeCharAt(node); 4801 case Builtin::kStringPrototypeCharCodeAt: 4802 return ReduceStringPrototypeStringAt(simplified()->StringCharCodeAt(), 4803 node); 4804 case Builtin::kStringPrototypeCodePointAt: 4805 return ReduceStringPrototypeStringAt(simplified()->StringCodePointAt(), 4806 node); 4807 case Builtin::kStringPrototypeSubstring: 4808 return ReduceStringPrototypeSubstring(node); 4809 case Builtin::kStringPrototypeSlice: 4810 return ReduceStringPrototypeSlice(node); 4811 case Builtin::kStringPrototypeSubstr: 4812 return ReduceStringPrototypeSubstr(node); 4813 case Builtin::kStringPrototypeStartsWith: 4814 return ReduceStringPrototypeStartsWith(node); 4815#ifdef V8_INTL_SUPPORT 4816 case Builtin::kStringPrototypeToLowerCaseIntl: 4817 return ReduceStringPrototypeToLowerCaseIntl(node); 4818 case Builtin::kStringPrototypeToUpperCaseIntl: 4819 return ReduceStringPrototypeToUpperCaseIntl(node); 4820#endif // V8_INTL_SUPPORT 4821 case Builtin::kStringFromCharCode: 4822 return ReduceStringFromCharCode(node); 4823 case Builtin::kStringFromCodePoint: 4824 return ReduceStringFromCodePoint(node); 4825 case Builtin::kStringPrototypeIterator: 4826 return ReduceStringPrototypeIterator(node); 4827 case Builtin::kStringPrototypeLocaleCompare: 4828 return ReduceStringPrototypeLocaleCompare(node); 4829 case Builtin::kStringIteratorPrototypeNext: 4830 return ReduceStringIteratorPrototypeNext(node); 4831 case Builtin::kStringPrototypeConcat: 4832 return ReduceStringPrototypeConcat(node); 4833 case Builtin::kTypedArrayPrototypeEntries: 4834 return ReduceArrayIterator(node, ArrayIteratorKind::kTypedArray, 4835 IterationKind::kEntries); 4836 case Builtin::kTypedArrayPrototypeKeys: 4837 return ReduceArrayIterator(node, ArrayIteratorKind::kTypedArray, 4838 IterationKind::kKeys); 4839 case Builtin::kTypedArrayPrototypeValues: 4840 return ReduceArrayIterator(node, ArrayIteratorKind::kTypedArray, 4841 IterationKind::kValues); 4842 case Builtin::kPromisePrototypeCatch: 4843 return ReducePromisePrototypeCatch(node); 4844 case Builtin::kPromisePrototypeFinally: 4845 return ReducePromisePrototypeFinally(node); 4846 case Builtin::kPromisePrototypeThen: 4847 return ReducePromisePrototypeThen(node); 4848 case Builtin::kPromiseResolveTrampoline: 4849 return ReducePromiseResolveTrampoline(node); 4850 case Builtin::kMapPrototypeEntries: 4851 return ReduceCollectionIteration(node, CollectionKind::kMap, 4852 IterationKind::kEntries); 4853 case Builtin::kMapPrototypeKeys: 4854 return ReduceCollectionIteration(node, CollectionKind::kMap, 4855 IterationKind::kKeys); 4856 case Builtin::kMapPrototypeGetSize: 4857 return ReduceCollectionPrototypeSize(node, CollectionKind::kMap); 4858 case Builtin::kMapPrototypeValues: 4859 return ReduceCollectionIteration(node, CollectionKind::kMap, 4860 IterationKind::kValues); 4861 case Builtin::kMapIteratorPrototypeNext: 4862 return ReduceCollectionIteratorPrototypeNext( 4863 node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(), 4864 FIRST_JS_MAP_ITERATOR_TYPE, LAST_JS_MAP_ITERATOR_TYPE); 4865 case Builtin::kSetPrototypeEntries: 4866 return ReduceCollectionIteration(node, CollectionKind::kSet, 4867 IterationKind::kEntries); 4868 case Builtin::kSetPrototypeGetSize: 4869 return ReduceCollectionPrototypeSize(node, CollectionKind::kSet); 4870 case Builtin::kSetPrototypeValues: 4871 return ReduceCollectionIteration(node, CollectionKind::kSet, 4872 IterationKind::kValues); 4873 case Builtin::kSetIteratorPrototypeNext: 4874 return ReduceCollectionIteratorPrototypeNext( 4875 node, OrderedHashSet::kEntrySize, factory()->empty_ordered_hash_set(), 4876 FIRST_JS_SET_ITERATOR_TYPE, LAST_JS_SET_ITERATOR_TYPE); 4877 case Builtin::kDatePrototypeGetTime: 4878 return ReduceDatePrototypeGetTime(node); 4879 case Builtin::kDateNow: 4880 return ReduceDateNow(node); 4881 case Builtin::kNumberConstructor: 4882 return ReduceNumberConstructor(node); 4883 case Builtin::kBigIntAsIntN: 4884 case Builtin::kBigIntAsUintN: 4885 return ReduceBigIntAsN(node, builtin); 4886 default: 4887 break; 4888 } 4889 4890 if (shared.function_template_info().has_value()) { 4891 return ReduceCallApiFunction(node, shared); 4892 } 4893 4894#if V8_ENABLE_WEBASSEMBLY 4895 if ((flags() & kInlineJSToWasmCalls) && shared.wasm_function_signature()) { 4896 return ReduceCallWasmFunction(node, shared); 4897 } 4898#endif // V8_ENABLE_WEBASSEMBLY 4899 4900 return NoChange(); 4901} 4902 4903TNode<Object> JSCallReducerAssembler::ReduceJSCallWithArrayLikeOrSpreadOfEmpty( 4904 std::unordered_set<Node*>* generated_calls_with_array_like_or_spread) { 4905 DCHECK_EQ(generated_calls_with_array_like_or_spread->count(node_ptr()), 0); 4906 JSCallWithArrayLikeOrSpreadNode n(node_ptr()); 4907 CallParameters const& p = n.Parameters(); 4908 TNode<Object> arguments_list = n.LastArgument(); 4909 DCHECK_EQ(static_cast<Node*>(arguments_list)->opcode(), 4910 IrOpcode::kJSCreateEmptyLiteralArray); 4911 4912 // Turn the JSCallWithArrayLike or JSCallWithSpread roughly into: 4913 // 4914 // "arguments_list array is still empty?" 4915 // | 4916 // | 4917 // Branch 4918 // / \ 4919 // / \ 4920 // IfTrue IfFalse 4921 // | | 4922 // | | 4923 // JSCall JSCallWithArrayLike/JSCallWithSpread 4924 // \ / 4925 // \ / 4926 // Merge 4927 4928 TNode<Number> length = TNode<Number>::UncheckedCast( 4929 LoadField(AccessBuilder::ForJSArrayLength(NO_ELEMENTS), arguments_list)); 4930 return SelectIf<Object>(NumberEqual(length, ZeroConstant())) 4931 .Then([&]() { 4932 TNode<Object> call = CopyNode(); 4933 static_cast<Node*>(call)->RemoveInput(n.LastArgumentIndex()); 4934 NodeProperties::ChangeOp( 4935 call, javascript()->Call(p.arity() - 1, p.frequency(), p.feedback(), 4936 p.convert_mode(), p.speculation_mode(), 4937 p.feedback_relation())); 4938 return call; 4939 }) 4940 .Else([&]() { 4941 TNode<Object> call = CopyNode(); 4942 generated_calls_with_array_like_or_spread->insert(call); 4943 return call; 4944 }) 4945 .ExpectFalse() 4946 .Value(); 4947} 4948 4949namespace { 4950 4951// Check if the target is a class constructor. 4952// We need to check all cases where the target will be typed as Function 4953// to prevent later optimizations from using the CallFunction trampoline, 4954// skipping the instance type check. 4955bool TargetIsClassConstructor(Node* node, JSHeapBroker* broker) { 4956 Node* target = NodeProperties::GetValueInput(node, 0); 4957 base::Optional<SharedFunctionInfoRef> shared; 4958 HeapObjectMatcher m(target); 4959 if (m.HasResolvedValue()) { 4960 ObjectRef target_ref = m.Ref(broker); 4961 if (target_ref.IsJSFunction()) { 4962 JSFunctionRef function = target_ref.AsJSFunction(); 4963 shared = function.shared(); 4964 } 4965 } else if (target->opcode() == IrOpcode::kJSCreateClosure) { 4966 CreateClosureParameters const& ccp = 4967 JSCreateClosureNode{target}.Parameters(); 4968 shared = ccp.shared_info(broker); 4969 } else if (target->opcode() == IrOpcode::kCheckClosure) { 4970 FeedbackCellRef cell = MakeRef(broker, FeedbackCellOf(target->op())); 4971 shared = cell.shared_function_info(); 4972 } 4973 4974 if (shared.has_value() && IsClassConstructor(shared->kind())) return true; 4975 4976 return false; 4977} 4978 4979} // namespace 4980 4981Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node) { 4982 JSCallWithArrayLikeNode n(node); 4983 CallParameters const& p = n.Parameters(); 4984 DCHECK_EQ(p.arity_without_implicit_args(), 1); // The arraylike object. 4985 // Class constructors are callable, but [[Call]] will raise an exception. 4986 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). 4987 if (TargetIsClassConstructor(node, broker())) { 4988 return NoChange(); 4989 } 4990 return ReduceCallOrConstructWithArrayLikeOrSpread( 4991 node, n.ArgumentCount(), n.LastArgumentIndex(), p.frequency(), 4992 p.feedback(), p.speculation_mode(), p.feedback_relation(), n.target(), 4993 n.effect(), n.control()); 4994} 4995 4996Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) { 4997 JSCallWithSpreadNode n(node); 4998 CallParameters const& p = n.Parameters(); 4999 DCHECK_GE(p.arity_without_implicit_args(), 1); // At least the spread. 5000 // Class constructors are callable, but [[Call]] will raise an exception. 5001 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). 5002 if (TargetIsClassConstructor(node, broker())) { 5003 return NoChange(); 5004 } 5005 return ReduceCallOrConstructWithArrayLikeOrSpread( 5006 node, n.ArgumentCount(), n.LastArgumentIndex(), p.frequency(), 5007 p.feedback(), p.speculation_mode(), p.feedback_relation(), n.target(), 5008 n.effect(), n.control()); 5009} 5010 5011Reduction JSCallReducer::ReduceJSConstruct(Node* node) { 5012 if (broker()->StackHasOverflowed()) return NoChange(); 5013 5014 JSConstructNode n(node); 5015 ConstructParameters const& p = n.Parameters(); 5016 int arity = p.arity_without_implicit_args(); 5017 Node* target = n.target(); 5018 Node* new_target = n.new_target(); 5019 Effect effect = n.effect(); 5020 Control control = n.control(); 5021 5022 if (p.feedback().IsValid()) { 5023 ProcessedFeedback const& feedback = 5024 broker()->GetFeedbackForCall(p.feedback()); 5025 if (feedback.IsInsufficient()) { 5026 return ReduceForInsufficientFeedback( 5027 node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct); 5028 } 5029 5030 base::Optional<HeapObjectRef> feedback_target = feedback.AsCall().target(); 5031 if (feedback_target.has_value() && feedback_target->IsAllocationSite()) { 5032 // The feedback is an AllocationSite, which means we have called the 5033 // Array function and collected transition (and pretenuring) feedback 5034 // for the resulting arrays. This has to be kept in sync with the 5035 // implementation in Ignition. 5036 5037 Node* array_function = 5038 jsgraph()->Constant(native_context().array_function()); 5039 5040 // Check that the {target} is still the {array_function}. 5041 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, 5042 array_function); 5043 effect = graph()->NewNode( 5044 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, 5045 effect, control); 5046 5047 // Turn the {node} into a {JSCreateArray} call. 5048 NodeProperties::ReplaceEffectInput(node, effect); 5049 STATIC_ASSERT(JSConstructNode::NewTargetIndex() == 1); 5050 node->ReplaceInput(n.NewTargetIndex(), array_function); 5051 node->RemoveInput(n.FeedbackVectorIndex()); 5052 NodeProperties::ChangeOp( 5053 node, javascript()->CreateArray(arity, 5054 feedback_target->AsAllocationSite())); 5055 return Changed(node); 5056 } else if (feedback_target.has_value() && 5057 !HeapObjectMatcher(new_target).HasResolvedValue() && 5058 feedback_target->map().is_constructor()) { 5059 Node* new_target_feedback = jsgraph()->Constant(*feedback_target); 5060 5061 // Check that the {new_target} is still the {new_target_feedback}. 5062 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), new_target, 5063 new_target_feedback); 5064 effect = graph()->NewNode( 5065 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, 5066 effect, control); 5067 5068 // Specialize the JSConstruct node to the {new_target_feedback}. 5069 node->ReplaceInput(n.NewTargetIndex(), new_target_feedback); 5070 NodeProperties::ReplaceEffectInput(node, effect); 5071 if (target == new_target) { 5072 node->ReplaceInput(n.TargetIndex(), new_target_feedback); 5073 } 5074 5075 // Try to further reduce the JSConstruct {node}. 5076 return Changed(node).FollowedBy(ReduceJSConstruct(node)); 5077 } 5078 } 5079 5080 // Try to specialize JSConstruct {node}s with constant {target}s. 5081 HeapObjectMatcher m(target); 5082 if (m.HasResolvedValue()) { 5083 HeapObjectRef target_ref = m.Ref(broker()); 5084 5085 // Raise a TypeError if the {target} is not a constructor. 5086 if (!target_ref.map().is_constructor()) { 5087 NodeProperties::ReplaceValueInputs(node, target); 5088 NodeProperties::ChangeOp(node, 5089 javascript()->CallRuntime( 5090 Runtime::kThrowConstructedNonConstructable)); 5091 return Changed(node); 5092 } 5093 5094 if (target_ref.IsJSFunction()) { 5095 JSFunctionRef function = target_ref.AsJSFunction(); 5096 5097 // Do not reduce constructors with break points. 5098 // If this state changes during background compilation, the compilation 5099 // job will be aborted from the main thread (see 5100 // Debug::PrepareFunctionForDebugExecution()). 5101 SharedFunctionInfoRef sfi = function.shared(); 5102 if (sfi.HasBreakInfo()) return NoChange(); 5103 5104 // Don't inline cross native context. 5105 if (!function.native_context().equals(native_context())) { 5106 return NoChange(); 5107 } 5108 5109 // Check for known builtin functions. 5110 Builtin builtin = 5111 sfi.HasBuiltinId() ? sfi.builtin_id() : Builtin::kNoBuiltinId; 5112 switch (builtin) { 5113 case Builtin::kArrayConstructor: { 5114 // TODO(bmeurer): Deal with Array subclasses here. 5115 // Turn the {node} into a {JSCreateArray} call. 5116 STATIC_ASSERT(JSConstructNode::NewTargetIndex() == 1); 5117 node->ReplaceInput(n.NewTargetIndex(), new_target); 5118 node->RemoveInput(n.FeedbackVectorIndex()); 5119 NodeProperties::ChangeOp( 5120 node, javascript()->CreateArray(arity, base::nullopt)); 5121 return Changed(node); 5122 } 5123 case Builtin::kObjectConstructor: { 5124 // If no value is passed, we can immediately lower to a simple 5125 // JSCreate and don't need to do any massaging of the {node}. 5126 if (arity == 0) { 5127 node->RemoveInput(n.FeedbackVectorIndex()); 5128 NodeProperties::ChangeOp(node, javascript()->Create()); 5129 return Changed(node); 5130 } 5131 5132 // If {target} is not the same as {new_target} (i.e. the Object 5133 // constructor), {value} will be ignored and therefore we can lower 5134 // to {JSCreate}. See https://tc39.es/ecma262/#sec-object-value. 5135 HeapObjectMatcher mnew_target(new_target); 5136 if (mnew_target.HasResolvedValue() && 5137 !mnew_target.Ref(broker()).equals(function)) { 5138 // Drop the value inputs. 5139 node->RemoveInput(n.FeedbackVectorIndex()); 5140 for (int i = n.ArgumentCount() - 1; i >= 0; i--) { 5141 node->RemoveInput(n.ArgumentIndex(i)); 5142 } 5143 NodeProperties::ChangeOp(node, javascript()->Create()); 5144 return Changed(node); 5145 } 5146 break; 5147 } 5148 case Builtin::kPromiseConstructor: 5149 return ReducePromiseConstructor(node); 5150 case Builtin::kTypedArrayConstructor: 5151 return ReduceTypedArrayConstructor(node, function.shared()); 5152 default: 5153 break; 5154 } 5155 } else if (target_ref.IsJSBoundFunction()) { 5156 JSBoundFunctionRef function = target_ref.AsJSBoundFunction(); 5157 JSReceiverRef bound_target_function = function.bound_target_function(); 5158 FixedArrayRef bound_arguments = function.bound_arguments(); 5159 const int bound_arguments_length = bound_arguments.length(); 5160 5161 // TODO(jgruber): Inline this block below once TryGet is guaranteed to 5162 // succeed. 5163 static constexpr int kInlineSize = 16; // Arbitrary. 5164 base::SmallVector<Node*, kInlineSize> args; 5165 for (int i = 0; i < bound_arguments_length; ++i) { 5166 base::Optional<ObjectRef> maybe_arg = bound_arguments.TryGet(i); 5167 if (!maybe_arg.has_value()) { 5168 TRACE_BROKER_MISSING(broker(), "bound argument"); 5169 return NoChange(); 5170 } 5171 args.emplace_back(jsgraph()->Constant(maybe_arg.value())); 5172 } 5173 5174 // Patch {node} to use [[BoundTargetFunction]]. 5175 node->ReplaceInput(n.TargetIndex(), 5176 jsgraph()->Constant(bound_target_function)); 5177 5178 // Patch {node} to use [[BoundTargetFunction]] 5179 // as new.target if {new_target} equals {target}. 5180 if (target == new_target) { 5181 node->ReplaceInput(n.NewTargetIndex(), 5182 jsgraph()->Constant(bound_target_function)); 5183 } else { 5184 node->ReplaceInput( 5185 n.NewTargetIndex(), 5186 graph()->NewNode(common()->Select(MachineRepresentation::kTagged), 5187 graph()->NewNode(simplified()->ReferenceEqual(), 5188 target, new_target), 5189 jsgraph()->Constant(bound_target_function), 5190 new_target)); 5191 } 5192 5193 // Insert the [[BoundArguments]] for {node}. 5194 for (int i = 0; i < bound_arguments_length; ++i) { 5195 node->InsertInput(graph()->zone(), n.ArgumentIndex(i), args[i]); 5196 arity++; 5197 } 5198 5199 // Update the JSConstruct operator on {node}. 5200 NodeProperties::ChangeOp( 5201 node, javascript()->Construct(JSConstructNode::ArityForArgc(arity), 5202 p.frequency(), FeedbackSource())); 5203 5204 // Try to further reduce the JSConstruct {node}. 5205 return Changed(node).FollowedBy(ReduceJSConstruct(node)); 5206 } 5207 5208 // TODO(bmeurer): Also support optimizing proxies here. 5209 } 5210 5211 // If {target} is the result of a JSCreateBoundFunction operation, 5212 // we can just fold the construction and construct the bound target 5213 // function directly instead. 5214 if (target->opcode() == IrOpcode::kJSCreateBoundFunction) { 5215 Node* bound_target_function = NodeProperties::GetValueInput(target, 0); 5216 int const bound_arguments_length = 5217 static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity()); 5218 5219 // Patch the {node} to use [[BoundTargetFunction]]. 5220 node->ReplaceInput(n.TargetIndex(), bound_target_function); 5221 5222 // Patch {node} to use [[BoundTargetFunction]] 5223 // as new.target if {new_target} equals {target}. 5224 if (target == new_target) { 5225 node->ReplaceInput(n.NewTargetIndex(), bound_target_function); 5226 } else { 5227 node->ReplaceInput( 5228 n.NewTargetIndex(), 5229 graph()->NewNode(common()->Select(MachineRepresentation::kTagged), 5230 graph()->NewNode(simplified()->ReferenceEqual(), 5231 target, new_target), 5232 bound_target_function, new_target)); 5233 } 5234 5235 // Insert the [[BoundArguments]] for {node}. 5236 for (int i = 0; i < bound_arguments_length; ++i) { 5237 Node* value = NodeProperties::GetValueInput(target, 2 + i); 5238 node->InsertInput(graph()->zone(), n.ArgumentIndex(i), value); 5239 arity++; 5240 } 5241 5242 // Update the JSConstruct operator on {node}. 5243 NodeProperties::ChangeOp( 5244 node, javascript()->Construct(JSConstructNode::ArityForArgc(arity), 5245 p.frequency(), FeedbackSource())); 5246 5247 // Try to further reduce the JSConstruct {node}. 5248 return Changed(node).FollowedBy(ReduceJSConstruct(node)); 5249 } 5250 5251 return NoChange(); 5252} 5253 5254// ES #sec-string.prototype.indexof 5255// ES #sec-string.prototype.includes 5256Reduction JSCallReducer::ReduceStringPrototypeIndexOfIncludes( 5257 Node* node, StringIndexOfIncludesVariant variant) { 5258 JSCallNode n(node); 5259 CallParameters const& p = n.Parameters(); 5260 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5261 return NoChange(); 5262 } 5263 5264 Effect effect = n.effect(); 5265 Control control = n.control(); 5266 if (n.ArgumentCount() > 0) { 5267 Node* receiver = n.receiver(); 5268 Node* new_receiver = effect = graph()->NewNode( 5269 simplified()->CheckString(p.feedback()), receiver, effect, control); 5270 5271 Node* search_string = n.Argument(0); 5272 Node* new_search_string = effect = 5273 graph()->NewNode(simplified()->CheckString(p.feedback()), search_string, 5274 effect, control); 5275 5276 Node* new_position = jsgraph()->ZeroConstant(); 5277 if (n.ArgumentCount() > 1) { 5278 Node* position = n.Argument(1); 5279 new_position = effect = graph()->NewNode( 5280 simplified()->CheckSmi(p.feedback()), position, effect, control); 5281 5282 Node* receiver_length = 5283 graph()->NewNode(simplified()->StringLength(), new_receiver); 5284 new_position = graph()->NewNode( 5285 simplified()->NumberMin(), 5286 graph()->NewNode(simplified()->NumberMax(), new_position, 5287 jsgraph()->ZeroConstant()), 5288 receiver_length); 5289 } 5290 5291 NodeProperties::ReplaceEffectInput(node, effect); 5292 RelaxEffectsAndControls(node); 5293 node->ReplaceInput(0, new_receiver); 5294 node->ReplaceInput(1, new_search_string); 5295 node->ReplaceInput(2, new_position); 5296 node->TrimInputCount(3); 5297 NodeProperties::ChangeOp(node, simplified()->StringIndexOf()); 5298 5299 if (variant == StringIndexOfIncludesVariant::kIndexOf) { 5300 return Changed(node); 5301 } else { 5302 DCHECK(variant == StringIndexOfIncludesVariant::kIncludes); 5303 Node* result = 5304 graph()->NewNode(simplified()->BooleanNot(), 5305 graph()->NewNode(simplified()->NumberEqual(), node, 5306 jsgraph()->SmiConstant(-1))); 5307 return Replace(result); 5308 } 5309 } 5310 return NoChange(); 5311} 5312 5313// ES #sec-string.prototype.substring 5314Reduction JSCallReducer::ReduceStringPrototypeSubstring(Node* node) { 5315 JSCallNode n(node); 5316 CallParameters const& p = n.Parameters(); 5317 if (n.ArgumentCount() < 1) return NoChange(); 5318 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5319 return NoChange(); 5320 } 5321 5322 JSCallReducerAssembler a(this, node); 5323 Node* subgraph = a.ReduceStringPrototypeSubstring(); 5324 return ReplaceWithSubgraph(&a, subgraph); 5325} 5326 5327// ES #sec-string.prototype.slice 5328Reduction JSCallReducer::ReduceStringPrototypeSlice(Node* node) { 5329 JSCallNode n(node); 5330 CallParameters const& p = n.Parameters(); 5331 if (n.ArgumentCount() < 1) return NoChange(); 5332 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5333 return NoChange(); 5334 } 5335 5336 JSCallReducerAssembler a(this, node); 5337 Node* subgraph = a.ReduceStringPrototypeSlice(); 5338 return ReplaceWithSubgraph(&a, subgraph); 5339} 5340 5341// ES #sec-string.prototype.substr 5342Reduction JSCallReducer::ReduceStringPrototypeSubstr(Node* node) { 5343 JSCallNode n(node); 5344 CallParameters const& p = n.Parameters(); 5345 if (n.ArgumentCount() < 1) return NoChange(); 5346 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5347 return NoChange(); 5348 } 5349 5350 Effect effect = n.effect(); 5351 Control control = n.control(); 5352 Node* receiver = n.receiver(); 5353 Node* start = n.Argument(0); 5354 Node* end = n.ArgumentOrUndefined(1, jsgraph()); 5355 5356 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), 5357 receiver, effect, control); 5358 5359 start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start, 5360 effect, control); 5361 5362 Node* length = graph()->NewNode(simplified()->StringLength(), receiver); 5363 5364 // Replace {end} argument with {length} if it is undefined. 5365 { 5366 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end, 5367 jsgraph()->UndefinedConstant()); 5368 Node* branch = 5369 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 5370 5371 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 5372 Node* etrue = effect; 5373 Node* vtrue = length; 5374 5375 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 5376 Node* efalse = effect; 5377 Node* vfalse = efalse = graph()->NewNode( 5378 simplified()->CheckSmi(p.feedback()), end, efalse, if_false); 5379 5380 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 5381 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 5382 end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 5383 vtrue, vfalse, control); 5384 } 5385 5386 Node* initStart = graph()->NewNode( 5387 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), 5388 graph()->NewNode(simplified()->NumberLessThan(), start, 5389 jsgraph()->ZeroConstant()), 5390 graph()->NewNode( 5391 simplified()->NumberMax(), 5392 graph()->NewNode(simplified()->NumberAdd(), length, start), 5393 jsgraph()->ZeroConstant()), 5394 start); 5395 // The select above guarantees that initStart is non-negative, but 5396 // our typer can't figure that out yet. 5397 initStart = effect = graph()->NewNode( 5398 common()->TypeGuard(Type::UnsignedSmall()), initStart, effect, control); 5399 5400 Node* resultLength = graph()->NewNode( 5401 simplified()->NumberMin(), 5402 graph()->NewNode(simplified()->NumberMax(), end, 5403 jsgraph()->ZeroConstant()), 5404 graph()->NewNode(simplified()->NumberSubtract(), length, initStart)); 5405 5406 // The the select below uses {resultLength} only if {resultLength > 0}, 5407 // but our typer can't figure that out yet. 5408 Node* to = effect = graph()->NewNode( 5409 common()->TypeGuard(Type::UnsignedSmall()), 5410 graph()->NewNode(simplified()->NumberAdd(), initStart, resultLength), 5411 effect, control); 5412 5413 Node* result_string = nullptr; 5414 // Return empty string if {from} is smaller than {to}. 5415 { 5416 Node* check = graph()->NewNode(simplified()->NumberLessThan(), 5417 jsgraph()->ZeroConstant(), resultLength); 5418 5419 Node* branch = 5420 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 5421 5422 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 5423 Node* etrue = effect; 5424 Node* vtrue = etrue = 5425 graph()->NewNode(simplified()->StringSubstring(), receiver, initStart, 5426 to, etrue, if_true); 5427 5428 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 5429 Node* efalse = effect; 5430 Node* vfalse = jsgraph()->EmptyStringConstant(); 5431 5432 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 5433 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 5434 result_string = 5435 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 5436 vtrue, vfalse, control); 5437 } 5438 5439 ReplaceWithValue(node, result_string, effect, control); 5440 return Replace(result_string); 5441} 5442 5443Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) { 5444 JSConstructWithArrayLikeNode n(node); 5445 ConstructParameters const& p = n.Parameters(); 5446 const int arraylike_index = n.LastArgumentIndex(); 5447 DCHECK_EQ(n.ArgumentCount(), 1); // The arraylike object. 5448 return ReduceCallOrConstructWithArrayLikeOrSpread( 5449 node, n.ArgumentCount(), arraylike_index, p.frequency(), p.feedback(), 5450 SpeculationMode::kDisallowSpeculation, CallFeedbackRelation::kTarget, 5451 n.target(), n.effect(), n.control()); 5452} 5453 5454Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { 5455 JSConstructWithSpreadNode n(node); 5456 ConstructParameters const& p = n.Parameters(); 5457 const int spread_index = n.LastArgumentIndex(); 5458 DCHECK_GE(n.ArgumentCount(), 1); // At least the spread. 5459 return ReduceCallOrConstructWithArrayLikeOrSpread( 5460 node, n.ArgumentCount(), spread_index, p.frequency(), p.feedback(), 5461 SpeculationMode::kDisallowSpeculation, CallFeedbackRelation::kTarget, 5462 n.target(), n.effect(), n.control()); 5463} 5464 5465Reduction JSCallReducer::ReduceReturnReceiver(Node* node) { 5466 JSCallNode n(node); 5467 Node* receiver = n.receiver(); 5468 ReplaceWithValue(node, receiver); 5469 return Replace(receiver); 5470} 5471 5472Reduction JSCallReducer::ReduceForInsufficientFeedback( 5473 Node* node, DeoptimizeReason reason) { 5474 DCHECK(node->opcode() == IrOpcode::kJSCall || 5475 node->opcode() == IrOpcode::kJSConstruct); 5476 if (!(flags() & kBailoutOnUninitialized)) return NoChange(); 5477 5478 Node* effect = NodeProperties::GetEffectInput(node); 5479 Node* control = NodeProperties::GetControlInput(node); 5480 Node* frame_state = 5481 NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead()); 5482 Node* deoptimize = 5483 graph()->NewNode(common()->Deoptimize(reason, FeedbackSource()), 5484 frame_state, effect, control); 5485 // TODO(bmeurer): This should be on the AdvancedReducer somehow. 5486 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); 5487 Revisit(graph()->end()); 5488 node->TrimInputCount(0); 5489 NodeProperties::ChangeOp(node, common()->Dead()); 5490 return Changed(node); 5491} 5492 5493Node* JSCallReducer::LoadReceiverElementsKind(Node* receiver, Effect* effect, 5494 Control control) { 5495 Node* effect_node = *effect; 5496 Node* receiver_map = effect_node = 5497 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), 5498 receiver, effect_node, control); 5499 Node* receiver_bit_field2 = effect_node = graph()->NewNode( 5500 simplified()->LoadField(AccessBuilder::ForMapBitField2()), receiver_map, 5501 effect_node, control); 5502 Node* receiver_elements_kind = graph()->NewNode( 5503 simplified()->NumberShiftRightLogical(), 5504 graph()->NewNode( 5505 simplified()->NumberBitwiseAnd(), receiver_bit_field2, 5506 jsgraph()->Constant(Map::Bits2::ElementsKindBits::kMask)), 5507 jsgraph()->Constant(Map::Bits2::ElementsKindBits::kShift)); 5508 *effect = effect_node; 5509 return receiver_elements_kind; 5510} 5511 5512void JSCallReducer::CheckIfElementsKind(Node* receiver_elements_kind, 5513 ElementsKind kind, Node* control, 5514 Node** if_true, Node** if_false) { 5515 Node* is_packed_kind = 5516 graph()->NewNode(simplified()->NumberEqual(), receiver_elements_kind, 5517 jsgraph()->Constant(GetPackedElementsKind(kind))); 5518 Node* packed_branch = 5519 graph()->NewNode(common()->Branch(), is_packed_kind, control); 5520 Node* if_packed = graph()->NewNode(common()->IfTrue(), packed_branch); 5521 5522 if (IsHoleyElementsKind(kind)) { 5523 Node* if_not_packed = graph()->NewNode(common()->IfFalse(), packed_branch); 5524 Node* is_holey_kind = 5525 graph()->NewNode(simplified()->NumberEqual(), receiver_elements_kind, 5526 jsgraph()->Constant(GetHoleyElementsKind(kind))); 5527 Node* holey_branch = 5528 graph()->NewNode(common()->Branch(), is_holey_kind, if_not_packed); 5529 Node* if_holey = graph()->NewNode(common()->IfTrue(), holey_branch); 5530 5531 Node* if_not_packed_not_holey = 5532 graph()->NewNode(common()->IfFalse(), holey_branch); 5533 5534 *if_true = graph()->NewNode(common()->Merge(2), if_packed, if_holey); 5535 *if_false = if_not_packed_not_holey; 5536 } else { 5537 *if_true = if_packed; 5538 *if_false = graph()->NewNode(common()->IfFalse(), packed_branch); 5539 } 5540} 5541 5542// ES6 section 22.1.3.18 Array.prototype.push ( ) 5543Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node) { 5544 JSCallNode n(node); 5545 CallParameters const& p = n.Parameters(); 5546 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5547 return NoChange(); 5548 } 5549 5550 int const num_values = n.ArgumentCount(); 5551 Node* receiver = n.receiver(); 5552 Effect effect = n.effect(); 5553 Control control = n.control(); 5554 5555 MapInference inference(broker(), receiver, effect); 5556 if (!inference.HaveMaps()) return NoChange(); 5557 ZoneVector<MapRef> const& receiver_maps = inference.GetMaps(); 5558 5559 std::vector<ElementsKind> kinds; 5560 if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kinds, true)) { 5561 return inference.NoChange(); 5562 } 5563 if (!dependencies()->DependOnNoElementsProtector()) { 5564 return inference.NoChange(); 5565 } 5566 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 5567 control, p.feedback()); 5568 5569 std::vector<Node*> controls_to_merge; 5570 std::vector<Node*> effects_to_merge; 5571 std::vector<Node*> values_to_merge; 5572 Node* return_value = jsgraph()->UndefinedConstant(); 5573 5574 Node* receiver_elements_kind = 5575 LoadReceiverElementsKind(receiver, &effect, control); 5576 Node* next_control = control; 5577 Node* next_effect = effect; 5578 for (size_t i = 0; i < kinds.size(); i++) { 5579 ElementsKind kind = kinds[i]; 5580 control = next_control; 5581 effect = next_effect; 5582 // We do not need branch for the last elements kind. 5583 if (i != kinds.size() - 1) { 5584 Node* control_node = control; 5585 CheckIfElementsKind(receiver_elements_kind, kind, control_node, 5586 &control_node, &next_control); 5587 control = control_node; 5588 } 5589 5590 // Collect the value inputs to push. 5591 std::vector<Node*> values(num_values); 5592 for (int j = 0; j < num_values; ++j) { 5593 values[j] = n.Argument(j); 5594 } 5595 5596 for (auto& value : values) { 5597 if (IsSmiElementsKind(kind)) { 5598 value = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), 5599 value, effect, control); 5600 } else if (IsDoubleElementsKind(kind)) { 5601 value = effect = graph()->NewNode( 5602 simplified()->CheckNumber(p.feedback()), value, effect, control); 5603 // Make sure we do not store signaling NaNs into double arrays. 5604 value = graph()->NewNode(simplified()->NumberSilenceNaN(), value); 5605 } 5606 } 5607 5608 // Load the "length" property of the {receiver}. 5609 Node* length = effect = graph()->NewNode( 5610 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), 5611 receiver, effect, control); 5612 return_value = length; 5613 5614 // Check if we have any {values} to push. 5615 if (num_values > 0) { 5616 // Compute the resulting "length" of the {receiver}. 5617 Node* new_length = return_value = graph()->NewNode( 5618 simplified()->NumberAdd(), length, jsgraph()->Constant(num_values)); 5619 5620 // Load the elements backing store of the {receiver}. 5621 Node* elements = effect = graph()->NewNode( 5622 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), 5623 receiver, effect, control); 5624 Node* elements_length = effect = graph()->NewNode( 5625 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), 5626 elements, effect, control); 5627 5628 GrowFastElementsMode mode = 5629 IsDoubleElementsKind(kind) 5630 ? GrowFastElementsMode::kDoubleElements 5631 : GrowFastElementsMode::kSmiOrObjectElements; 5632 elements = effect = graph()->NewNode( 5633 simplified()->MaybeGrowFastElements(mode, p.feedback()), receiver, 5634 elements, 5635 graph()->NewNode(simplified()->NumberAdd(), length, 5636 jsgraph()->Constant(num_values - 1)), 5637 elements_length, effect, control); 5638 5639 // Update the JSArray::length field. Since this is observable, 5640 // there must be no other check after this. 5641 effect = graph()->NewNode( 5642 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), 5643 receiver, new_length, effect, control); 5644 5645 // Append the {values} to the {elements}. 5646 for (int j = 0; j < num_values; ++j) { 5647 Node* value = values[j]; 5648 Node* index = graph()->NewNode(simplified()->NumberAdd(), length, 5649 jsgraph()->Constant(j)); 5650 effect = 5651 graph()->NewNode(simplified()->StoreElement( 5652 AccessBuilder::ForFixedArrayElement(kind)), 5653 elements, index, value, effect, control); 5654 } 5655 } 5656 5657 controls_to_merge.push_back(control); 5658 effects_to_merge.push_back(effect); 5659 values_to_merge.push_back(return_value); 5660 } 5661 5662 if (controls_to_merge.size() > 1) { 5663 int const count = static_cast<int>(controls_to_merge.size()); 5664 5665 control = graph()->NewNode(common()->Merge(count), count, 5666 &controls_to_merge.front()); 5667 effects_to_merge.push_back(control); 5668 effect = graph()->NewNode(common()->EffectPhi(count), count + 1, 5669 &effects_to_merge.front()); 5670 values_to_merge.push_back(control); 5671 return_value = 5672 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count), 5673 count + 1, &values_to_merge.front()); 5674 } 5675 5676 ReplaceWithValue(node, return_value, effect, control); 5677 return Replace(return_value); 5678} 5679 5680// ES6 section 22.1.3.17 Array.prototype.pop ( ) 5681Reduction JSCallReducer::ReduceArrayPrototypePop(Node* node) { 5682 JSCallNode n(node); 5683 CallParameters const& p = n.Parameters(); 5684 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5685 return NoChange(); 5686 } 5687 5688 Effect effect = n.effect(); 5689 Control control = n.control(); 5690 Node* receiver = n.receiver(); 5691 5692 MapInference inference(broker(), receiver, effect); 5693 if (!inference.HaveMaps()) return NoChange(); 5694 ZoneVector<MapRef> const& receiver_maps = inference.GetMaps(); 5695 5696 std::vector<ElementsKind> kinds; 5697 if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kinds)) { 5698 return inference.NoChange(); 5699 } 5700 if (!dependencies()->DependOnNoElementsProtector()) { 5701 return inference.NoChange(); 5702 } 5703 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 5704 control, p.feedback()); 5705 5706 std::vector<Node*> controls_to_merge; 5707 std::vector<Node*> effects_to_merge; 5708 std::vector<Node*> values_to_merge; 5709 Node* value = jsgraph()->UndefinedConstant(); 5710 5711 Node* receiver_elements_kind = 5712 LoadReceiverElementsKind(receiver, &effect, control); 5713 Node* next_control = control; 5714 Node* next_effect = effect; 5715 for (size_t i = 0; i < kinds.size(); i++) { 5716 ElementsKind kind = kinds[i]; 5717 control = next_control; 5718 effect = next_effect; 5719 // We do not need branch for the last elements kind. 5720 if (i != kinds.size() - 1) { 5721 Node* control_node = control; 5722 CheckIfElementsKind(receiver_elements_kind, kind, control_node, 5723 &control_node, &next_control); 5724 control = control_node; 5725 } 5726 5727 // Load the "length" property of the {receiver}. 5728 Node* length = effect = graph()->NewNode( 5729 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), 5730 receiver, effect, control); 5731 5732 // Check if the {receiver} has any elements. 5733 Node* check = graph()->NewNode(simplified()->NumberEqual(), length, 5734 jsgraph()->ZeroConstant()); 5735 Node* branch = 5736 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); 5737 5738 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 5739 Node* etrue = effect; 5740 Node* vtrue = jsgraph()->UndefinedConstant(); 5741 5742 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 5743 Node* efalse = effect; 5744 Node* vfalse; 5745 { 5746 // TODO(turbofan): We should trim the backing store if the capacity is too 5747 // big, as implemented in elements.cc:ElementsAccessorBase::SetLengthImpl. 5748 5749 // Load the elements backing store from the {receiver}. 5750 Node* elements = efalse = graph()->NewNode( 5751 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), 5752 receiver, efalse, if_false); 5753 5754 // Ensure that we aren't popping from a copy-on-write backing store. 5755 if (IsSmiOrObjectElementsKind(kind)) { 5756 elements = efalse = 5757 graph()->NewNode(simplified()->EnsureWritableFastElements(), 5758 receiver, elements, efalse, if_false); 5759 } 5760 5761 // Compute the new {length}. 5762 Node* new_length = graph()->NewNode(simplified()->NumberSubtract(), 5763 length, jsgraph()->OneConstant()); 5764 5765 // This extra check exists solely to break an exploitation technique 5766 // that abuses typer mismatches. 5767 new_length = efalse = graph()->NewNode( 5768 simplified()->CheckBounds(p.feedback(), 5769 CheckBoundsFlag::kAbortOnOutOfBounds), 5770 new_length, length, efalse, if_false); 5771 5772 // Store the new {length} to the {receiver}. 5773 efalse = graph()->NewNode( 5774 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), 5775 receiver, new_length, efalse, if_false); 5776 5777 // Load the last entry from the {elements}. 5778 vfalse = efalse = graph()->NewNode( 5779 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)), 5780 elements, new_length, efalse, if_false); 5781 5782 // Store a hole to the element we just removed from the {receiver}. 5783 efalse = graph()->NewNode( 5784 simplified()->StoreElement( 5785 AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))), 5786 elements, new_length, jsgraph()->TheHoleConstant(), efalse, if_false); 5787 } 5788 5789 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 5790 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 5791 value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 5792 vtrue, vfalse, control); 5793 5794 // Convert the hole to undefined. Do this last, so that we can optimize 5795 // conversion operator via some smart strength reduction in many cases. 5796 if (IsHoleyElementsKind(kind)) { 5797 value = 5798 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value); 5799 } 5800 5801 controls_to_merge.push_back(control); 5802 effects_to_merge.push_back(effect); 5803 values_to_merge.push_back(value); 5804 } 5805 5806 if (controls_to_merge.size() > 1) { 5807 int const count = static_cast<int>(controls_to_merge.size()); 5808 5809 control = graph()->NewNode(common()->Merge(count), count, 5810 &controls_to_merge.front()); 5811 effects_to_merge.push_back(control); 5812 effect = graph()->NewNode(common()->EffectPhi(count), count + 1, 5813 &effects_to_merge.front()); 5814 values_to_merge.push_back(control); 5815 value = 5816 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count), 5817 count + 1, &values_to_merge.front()); 5818 } 5819 5820 ReplaceWithValue(node, value, effect, control); 5821 return Replace(value); 5822} 5823 5824// ES6 section 22.1.3.22 Array.prototype.shift ( ) 5825Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) { 5826 JSCallNode n(node); 5827 CallParameters const& p = n.Parameters(); 5828 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 5829 return NoChange(); 5830 } 5831 5832 Node* target = n.target(); 5833 Node* receiver = n.receiver(); 5834 Node* context = n.context(); 5835 FrameState frame_state = n.frame_state(); 5836 Effect effect = n.effect(); 5837 Control control = n.control(); 5838 5839 MapInference inference(broker(), receiver, effect); 5840 if (!inference.HaveMaps()) return NoChange(); 5841 ZoneVector<MapRef> const& receiver_maps = inference.GetMaps(); 5842 5843 std::vector<ElementsKind> kinds; 5844 if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kinds)) { 5845 return inference.NoChange(); 5846 } 5847 if (!dependencies()->DependOnNoElementsProtector()) { 5848 return inference.NoChange(); 5849 } 5850 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 5851 control, p.feedback()); 5852 5853 std::vector<Node*> controls_to_merge; 5854 std::vector<Node*> effects_to_merge; 5855 std::vector<Node*> values_to_merge; 5856 Node* value = jsgraph()->UndefinedConstant(); 5857 5858 Node* receiver_elements_kind = 5859 LoadReceiverElementsKind(receiver, &effect, control); 5860 Node* next_control = control; 5861 Node* next_effect = effect; 5862 for (size_t i = 0; i < kinds.size(); i++) { 5863 ElementsKind kind = kinds[i]; 5864 control = next_control; 5865 effect = next_effect; 5866 // We do not need branch for the last elements kind. 5867 if (i != kinds.size() - 1) { 5868 Node* control_node = control; 5869 CheckIfElementsKind(receiver_elements_kind, kind, control_node, 5870 &control_node, &next_control); 5871 control = control_node; 5872 } 5873 5874 // Load length of the {receiver}. 5875 Node* length = effect = graph()->NewNode( 5876 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), 5877 receiver, effect, control); 5878 5879 // Return undefined if {receiver} has no elements. 5880 Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length, 5881 jsgraph()->ZeroConstant()); 5882 Node* branch0 = 5883 graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); 5884 5885 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); 5886 Node* etrue0 = effect; 5887 Node* vtrue0 = jsgraph()->UndefinedConstant(); 5888 5889 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); 5890 Node* efalse0 = effect; 5891 Node* vfalse0; 5892 { 5893 // Check if we should take the fast-path. 5894 Node* check1 = 5895 graph()->NewNode(simplified()->NumberLessThanOrEqual(), length, 5896 jsgraph()->Constant(JSArray::kMaxCopyElements)); 5897 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue), 5898 check1, if_false0); 5899 5900 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); 5901 Node* etrue1 = efalse0; 5902 Node* vtrue1; 5903 { 5904 Node* elements = etrue1 = graph()->NewNode( 5905 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), 5906 receiver, etrue1, if_true1); 5907 5908 // Load the first element here, which we return below. 5909 vtrue1 = etrue1 = graph()->NewNode( 5910 simplified()->LoadElement( 5911 AccessBuilder::ForFixedArrayElement(kind)), 5912 elements, jsgraph()->ZeroConstant(), etrue1, if_true1); 5913 5914 // Ensure that we aren't shifting a copy-on-write backing store. 5915 if (IsSmiOrObjectElementsKind(kind)) { 5916 elements = etrue1 = 5917 graph()->NewNode(simplified()->EnsureWritableFastElements(), 5918 receiver, elements, etrue1, if_true1); 5919 } 5920 5921 // Shift the remaining {elements} by one towards the start. 5922 Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1); 5923 Node* eloop = 5924 graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop); 5925 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); 5926 NodeProperties::MergeControlToEnd(graph(), common(), terminate); 5927 5928 Node* index = graph()->NewNode( 5929 common()->Phi(MachineRepresentation::kTagged, 2), 5930 jsgraph()->OneConstant(), 5931 jsgraph()->Constant(JSArray::kMaxCopyElements - 1), loop); 5932 5933 { 5934 Node* check2 = 5935 graph()->NewNode(simplified()->NumberLessThan(), index, length); 5936 Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop); 5937 5938 if_true1 = graph()->NewNode(common()->IfFalse(), branch2); 5939 etrue1 = eloop; 5940 5941 Node* control2 = graph()->NewNode(common()->IfTrue(), branch2); 5942 Node* effect2 = etrue1; 5943 5944 ElementAccess const access = 5945 AccessBuilder::ForFixedArrayElement(kind); 5946 5947 // When disable FLAG_turbo_loop_variable, typer cannot infer index 5948 // is in [1, kMaxCopyElements-1], and will break in representing 5949 // kRepFloat64 (Range(1, inf)) to kRepWord64 when converting 5950 // input for kLoadElement. So we need to add type guard here. 5951 // And we need to use index when using NumberLessThan to check 5952 // terminate and updating index, otherwise which will break inducing 5953 // variables in LoopVariableOptimizer. 5954 STATIC_ASSERT(JSArray::kMaxCopyElements < kSmiMaxValue); 5955 Node* index_retyped = effect2 = 5956 graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()), 5957 index, effect2, control2); 5958 5959 Node* value2 = effect2 = 5960 graph()->NewNode(simplified()->LoadElement(access), elements, 5961 index_retyped, effect2, control2); 5962 effect2 = graph()->NewNode( 5963 simplified()->StoreElement(access), elements, 5964 graph()->NewNode(simplified()->NumberSubtract(), index_retyped, 5965 jsgraph()->OneConstant()), 5966 value2, effect2, control2); 5967 5968 loop->ReplaceInput(1, control2); 5969 eloop->ReplaceInput(1, effect2); 5970 index->ReplaceInput(1, 5971 graph()->NewNode(simplified()->NumberAdd(), index, 5972 jsgraph()->OneConstant())); 5973 } 5974 5975 // Compute the new {length}. 5976 Node* new_length = graph()->NewNode(simplified()->NumberSubtract(), 5977 length, jsgraph()->OneConstant()); 5978 5979 // This extra check exists solely to break an exploitation technique 5980 // that abuses typer mismatches. 5981 new_length = etrue1 = graph()->NewNode( 5982 simplified()->CheckBounds(p.feedback(), 5983 CheckBoundsFlag::kAbortOnOutOfBounds), 5984 new_length, length, etrue1, if_true1); 5985 5986 // Store the new {length} to the {receiver}. 5987 etrue1 = graph()->NewNode( 5988 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), 5989 receiver, new_length, etrue1, if_true1); 5990 5991 // Store a hole to the element we just removed from the {receiver}. 5992 etrue1 = graph()->NewNode( 5993 simplified()->StoreElement(AccessBuilder::ForFixedArrayElement( 5994 GetHoleyElementsKind(kind))), 5995 elements, new_length, jsgraph()->TheHoleConstant(), etrue1, 5996 if_true1); 5997 } 5998 5999 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); 6000 Node* efalse1 = efalse0; 6001 Node* vfalse1; 6002 { 6003 // Call the generic C++ implementation. 6004 const Builtin builtin = Builtin::kArrayShift; 6005 auto call_descriptor = Linkage::GetCEntryStubCallDescriptor( 6006 graph()->zone(), 1, BuiltinArguments::kNumExtraArgsWithReceiver, 6007 Builtins::name(builtin), node->op()->properties(), 6008 CallDescriptor::kNeedsFrameState); 6009 Node* stub_code = jsgraph()->CEntryStubConstant( 6010 1, SaveFPRegsMode::kIgnore, ArgvMode::kStack, true); 6011 Address builtin_entry = Builtins::CppEntryOf(builtin); 6012 Node* entry = jsgraph()->ExternalConstant( 6013 ExternalReference::Create(builtin_entry)); 6014 Node* argc = 6015 jsgraph()->Constant(BuiltinArguments::kNumExtraArgsWithReceiver); 6016 if_false1 = efalse1 = vfalse1 = 6017 graph()->NewNode(common()->Call(call_descriptor), stub_code, 6018 receiver, jsgraph()->PaddingConstant(), argc, 6019 target, jsgraph()->UndefinedConstant(), entry, 6020 argc, context, frame_state, efalse1, if_false1); 6021 } 6022 6023 if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); 6024 efalse0 = 6025 graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); 6026 vfalse0 = 6027 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 6028 vtrue1, vfalse1, if_false0); 6029 } 6030 6031 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); 6032 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); 6033 value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 6034 vtrue0, vfalse0, control); 6035 6036 // Convert the hole to undefined. Do this last, so that we can optimize 6037 // conversion operator via some smart strength reduction in many cases. 6038 if (IsHoleyElementsKind(kind)) { 6039 value = 6040 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value); 6041 } 6042 6043 controls_to_merge.push_back(control); 6044 effects_to_merge.push_back(effect); 6045 values_to_merge.push_back(value); 6046 } 6047 6048 if (controls_to_merge.size() > 1) { 6049 int const count = static_cast<int>(controls_to_merge.size()); 6050 6051 control = graph()->NewNode(common()->Merge(count), count, 6052 &controls_to_merge.front()); 6053 effects_to_merge.push_back(control); 6054 effect = graph()->NewNode(common()->EffectPhi(count), count + 1, 6055 &effects_to_merge.front()); 6056 values_to_merge.push_back(control); 6057 value = 6058 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count), 6059 count + 1, &values_to_merge.front()); 6060 } 6061 6062 ReplaceWithValue(node, value, effect, control); 6063 return Replace(value); 6064} 6065 6066// ES6 section 22.1.3.23 Array.prototype.slice ( ) 6067Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node) { 6068 if (!FLAG_turbo_inline_array_builtins) return NoChange(); 6069 JSCallNode n(node); 6070 CallParameters const& p = n.Parameters(); 6071 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6072 return NoChange(); 6073 } 6074 6075 Node* receiver = n.receiver(); 6076 Node* start = n.ArgumentOr(0, jsgraph()->ZeroConstant()); 6077 Node* end = n.ArgumentOrUndefined(1, jsgraph()); 6078 Node* context = n.context(); 6079 Effect effect = n.effect(); 6080 Control control = n.control(); 6081 6082 // Optimize for the case where we simply clone the {receiver}, i.e. when the 6083 // {start} is zero and the {end} is undefined (meaning it will be set to 6084 // {receiver}s "length" property). This logic should be in sync with 6085 // ReduceArrayPrototypeSlice (to a reasonable degree). This is because 6086 // CloneFastJSArray produces arrays which are potentially COW. If there's a 6087 // discrepancy, TF generates code which produces a COW array and then expects 6088 // it to be non-COW (or the other way around) -> immediate deopt. 6089 if (!NumberMatcher(start).Is(0) || 6090 !HeapObjectMatcher(end).Is(factory()->undefined_value())) { 6091 return NoChange(); 6092 } 6093 6094 MapInference inference(broker(), receiver, effect); 6095 if (!inference.HaveMaps()) return NoChange(); 6096 ZoneVector<MapRef> const& receiver_maps = inference.GetMaps(); 6097 6098 // Check that the maps are of JSArray (and more). 6099 // TODO(turbofan): Consider adding special case for the common pattern 6100 // `slice.call(arguments)`, for example jQuery makes heavy use of that. 6101 bool can_be_holey = false; 6102 for (const MapRef& receiver_map : receiver_maps) { 6103 if (!receiver_map.supports_fast_array_iteration()) { 6104 return inference.NoChange(); 6105 } 6106 if (IsHoleyElementsKind(receiver_map.elements_kind())) { 6107 can_be_holey = true; 6108 } 6109 } 6110 6111 if (!dependencies()->DependOnArraySpeciesProtector()) { 6112 return inference.NoChange(); 6113 } 6114 if (can_be_holey && !dependencies()->DependOnNoElementsProtector()) { 6115 return inference.NoChange(); 6116 } 6117 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 6118 control, p.feedback()); 6119 6120 // TODO(turbofan): We can do even better here, either adding a CloneArray 6121 // simplified operator, whose output type indicates that it's an Array, 6122 // saving subsequent checks, or yet better, by introducing new operators 6123 // CopySmiOrObjectElements / CopyDoubleElements and inlining the JSArray 6124 // allocation in here. That way we'd even get escape analysis and scalar 6125 // replacement to help in some cases. 6126 Callable callable = 6127 Builtins::CallableFor(isolate(), Builtin::kCloneFastJSArray); 6128 auto call_descriptor = Linkage::GetStubCallDescriptor( 6129 graph()->zone(), callable.descriptor(), 6130 callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags, 6131 Operator::kNoThrow | Operator::kNoDeopt); 6132 6133 // Calls to Builtin::kCloneFastJSArray produce COW arrays 6134 // if the original array is COW 6135 Node* clone = effect = graph()->NewNode( 6136 common()->Call(call_descriptor), jsgraph()->HeapConstant(callable.code()), 6137 receiver, context, effect, control); 6138 6139 ReplaceWithValue(node, clone, effect, control); 6140 return Replace(clone); 6141} 6142 6143// ES6 section 22.1.2.2 Array.isArray ( arg ) 6144Reduction JSCallReducer::ReduceArrayIsArray(Node* node) { 6145 // We certainly know that undefined is not an array. 6146 JSCallNode n(node); 6147 if (n.ArgumentCount() < 1) { 6148 Node* value = jsgraph()->FalseConstant(); 6149 ReplaceWithValue(node, value); 6150 return Replace(value); 6151 } 6152 6153 Effect effect = n.effect(); 6154 Control control = n.control(); 6155 Node* context = n.context(); 6156 FrameState frame_state = n.frame_state(); 6157 Node* object = n.Argument(0); 6158 node->ReplaceInput(0, object); 6159 node->ReplaceInput(1, context); 6160 node->ReplaceInput(2, frame_state); 6161 node->ReplaceInput(3, effect); 6162 node->ReplaceInput(4, control); 6163 node->TrimInputCount(5); 6164 NodeProperties::ChangeOp(node, javascript()->ObjectIsArray()); 6165 return Changed(node); 6166} 6167 6168Reduction JSCallReducer::ReduceArrayIterator(Node* node, 6169 ArrayIteratorKind array_kind, 6170 IterationKind iteration_kind) { 6171 JSCallNode n(node); 6172 Node* receiver = n.receiver(); 6173 Node* context = n.context(); 6174 Effect effect = n.effect(); 6175 Control control = n.control(); 6176 6177 // Check if we know that {receiver} is a valid JSReceiver. 6178 MapInference inference(broker(), receiver, effect); 6179 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAreJSReceiver()) { 6180 return NoChange(); 6181 } 6182 6183 // TypedArray iteration is stricter: it throws if the receiver is not a typed 6184 // array. So don't bother optimizing in that case. 6185 if (array_kind == ArrayIteratorKind::kTypedArray && 6186 !inference.AllOfInstanceTypesAre(InstanceType::JS_TYPED_ARRAY_TYPE)) { 6187 return NoChange(); 6188 } 6189 6190 if (array_kind == ArrayIteratorKind::kTypedArray) { 6191 // Make sure we deopt when the JSArrayBuffer is detached. 6192 if (!dependencies()->DependOnArrayBufferDetachingProtector()) { 6193 CallParameters const& p = CallParametersOf(node->op()); 6194 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6195 return NoChange(); 6196 } 6197 Node* buffer = effect = graph()->NewNode( 6198 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), 6199 receiver, effect, control); 6200 Node* buffer_bit_field = effect = graph()->NewNode( 6201 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), 6202 buffer, effect, control); 6203 Node* check = graph()->NewNode( 6204 simplified()->NumberEqual(), 6205 graph()->NewNode( 6206 simplified()->NumberBitwiseAnd(), buffer_bit_field, 6207 jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)), 6208 jsgraph()->ZeroConstant()); 6209 effect = graph()->NewNode( 6210 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached, 6211 p.feedback()), 6212 check, effect, control); 6213 } 6214 } 6215 6216 // Morph the {node} into a JSCreateArrayIterator with the given {kind}. 6217 RelaxControls(node); 6218 node->ReplaceInput(0, receiver); 6219 node->ReplaceInput(1, context); 6220 node->ReplaceInput(2, effect); 6221 node->ReplaceInput(3, control); 6222 node->TrimInputCount(4); 6223 NodeProperties::ChangeOp(node, 6224 javascript()->CreateArrayIterator(iteration_kind)); 6225 return Changed(node); 6226} 6227 6228// ES #sec-%arrayiteratorprototype%.next 6229Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { 6230 JSCallNode n(node); 6231 CallParameters const& p = n.Parameters(); 6232 Node* iterator = n.receiver(); 6233 Node* context = n.context(); 6234 Effect effect = n.effect(); 6235 Control control = n.control(); 6236 6237 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6238 return NoChange(); 6239 } 6240 6241 if (iterator->opcode() != IrOpcode::kJSCreateArrayIterator) return NoChange(); 6242 6243 IterationKind const iteration_kind = 6244 CreateArrayIteratorParametersOf(iterator->op()).kind(); 6245 Node* iterated_object = NodeProperties::GetValueInput(iterator, 0); 6246 Effect iterator_effect{NodeProperties::GetEffectInput(iterator)}; 6247 6248 MapInference inference(broker(), iterated_object, iterator_effect); 6249 if (!inference.HaveMaps()) return NoChange(); 6250 ZoneVector<MapRef> const& iterated_object_maps = inference.GetMaps(); 6251 6252 // Check that various {iterated_object_maps} have compatible elements kinds. 6253 ElementsKind elements_kind = iterated_object_maps[0].elements_kind(); 6254 if (IsTypedArrayElementsKind(elements_kind)) { 6255 // TurboFan doesn't support loading from BigInt typed arrays yet. 6256 if (elements_kind == BIGUINT64_ELEMENTS || 6257 elements_kind == BIGINT64_ELEMENTS) { 6258 return inference.NoChange(); 6259 } 6260 for (const MapRef& iterated_object_map : iterated_object_maps) { 6261 if (iterated_object_map.elements_kind() != elements_kind) { 6262 return inference.NoChange(); 6263 } 6264 } 6265 } else { 6266 if (!CanInlineArrayIteratingBuiltin(broker(), iterated_object_maps, 6267 &elements_kind)) { 6268 return inference.NoChange(); 6269 } 6270 } 6271 6272 if (IsHoleyElementsKind(elements_kind) && 6273 !dependencies()->DependOnNoElementsProtector()) { 6274 return inference.NoChange(); 6275 } 6276 6277 // Since the map inference was done relative to {iterator_effect} rather than 6278 // {effect}, we need to guard the use of the map(s) even when the inference 6279 // was reliable. 6280 inference.InsertMapChecks(jsgraph(), &effect, control, p.feedback()); 6281 6282 if (IsTypedArrayElementsKind(elements_kind)) { 6283 // See if we can skip the detaching check. 6284 if (!dependencies()->DependOnArrayBufferDetachingProtector()) { 6285 // Bail out if the {iterated_object}s JSArrayBuffer was detached. 6286 Node* buffer = effect = graph()->NewNode( 6287 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), 6288 iterated_object, effect, control); 6289 Node* buffer_bit_field = effect = graph()->NewNode( 6290 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), 6291 buffer, effect, control); 6292 Node* check = graph()->NewNode( 6293 simplified()->NumberEqual(), 6294 graph()->NewNode( 6295 simplified()->NumberBitwiseAnd(), buffer_bit_field, 6296 jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)), 6297 jsgraph()->ZeroConstant()); 6298 effect = graph()->NewNode( 6299 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached, 6300 p.feedback()), 6301 check, effect, control); 6302 } 6303 } 6304 6305 // Load the [[NextIndex]] from the {iterator} and leverage the fact 6306 // that we definitely know that it's in Unsigned32 range since the 6307 // {iterated_object} is either a JSArray or a JSTypedArray. For the 6308 // latter case we even know that it's a Smi in UnsignedSmall range. 6309 FieldAccess index_access = AccessBuilder::ForJSArrayIteratorNextIndex(); 6310 if (IsTypedArrayElementsKind(elements_kind)) { 6311 index_access.type = TypeCache::Get()->kJSTypedArrayLengthType; 6312 } else { 6313 index_access.type = TypeCache::Get()->kJSArrayLengthType; 6314 } 6315 Node* index = effect = graph()->NewNode(simplified()->LoadField(index_access), 6316 iterator, effect, control); 6317 6318 // Load the elements of the {iterated_object}. While it feels 6319 // counter-intuitive to place the elements pointer load before 6320 // the condition below, as it might not be needed (if the {index} 6321 // is out of bounds for the {iterated_object}), it's better this 6322 // way as it allows the LoadElimination to eliminate redundant 6323 // reloads of the elements pointer. 6324 Node* elements = effect = graph()->NewNode( 6325 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), 6326 iterated_object, effect, control); 6327 6328 // Load the length of the {iterated_object}. Due to the map checks we 6329 // already know something about the length here, which we can leverage 6330 // to generate Word32 operations below without additional checking. 6331 FieldAccess length_access = 6332 IsTypedArrayElementsKind(elements_kind) 6333 ? AccessBuilder::ForJSTypedArrayLength() 6334 : AccessBuilder::ForJSArrayLength(elements_kind); 6335 Node* length = effect = graph()->NewNode( 6336 simplified()->LoadField(length_access), iterated_object, effect, control); 6337 6338 // Check whether {index} is within the valid range for the {iterated_object}. 6339 Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length); 6340 Node* branch = 6341 graph()->NewNode(common()->Branch(BranchHint::kNone), check, control); 6342 6343 Node* done_true; 6344 Node* value_true; 6345 Node* etrue = effect; 6346 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 6347 { 6348 // This extra check exists to refine the type of {index} but also to break 6349 // an exploitation technique that abuses typer mismatches. 6350 index = etrue = graph()->NewNode( 6351 simplified()->CheckBounds(p.feedback(), 6352 CheckBoundsFlag::kAbortOnOutOfBounds), 6353 index, length, etrue, if_true); 6354 6355 done_true = jsgraph()->FalseConstant(); 6356 if (iteration_kind == IterationKind::kKeys) { 6357 // Just return the {index}. 6358 value_true = index; 6359 } else { 6360 DCHECK(iteration_kind == IterationKind::kEntries || 6361 iteration_kind == IterationKind::kValues); 6362 6363 if (IsTypedArrayElementsKind(elements_kind)) { 6364 Node* base_ptr = etrue = 6365 graph()->NewNode(simplified()->LoadField( 6366 AccessBuilder::ForJSTypedArrayBasePointer()), 6367 iterated_object, etrue, if_true); 6368 Node* external_ptr = etrue = graph()->NewNode( 6369 simplified()->LoadField( 6370 AccessBuilder::ForJSTypedArrayExternalPointer()), 6371 iterated_object, etrue, if_true); 6372 6373 ExternalArrayType array_type = kExternalInt8Array; 6374 switch (elements_kind) { 6375#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ 6376 case TYPE##_ELEMENTS: \ 6377 array_type = kExternal##Type##Array; \ 6378 break; 6379 TYPED_ARRAYS(TYPED_ARRAY_CASE) 6380 default: 6381 UNREACHABLE(); 6382#undef TYPED_ARRAY_CASE 6383 } 6384 6385 Node* buffer = etrue = 6386 graph()->NewNode(simplified()->LoadField( 6387 AccessBuilder::ForJSArrayBufferViewBuffer()), 6388 iterated_object, etrue, if_true); 6389 6390 value_true = etrue = 6391 graph()->NewNode(simplified()->LoadTypedElement(array_type), buffer, 6392 base_ptr, external_ptr, index, etrue, if_true); 6393 } else { 6394 value_true = etrue = graph()->NewNode( 6395 simplified()->LoadElement( 6396 AccessBuilder::ForFixedArrayElement(elements_kind)), 6397 elements, index, etrue, if_true); 6398 6399 // Convert hole to undefined if needed. 6400 if (elements_kind == HOLEY_ELEMENTS || 6401 elements_kind == HOLEY_SMI_ELEMENTS) { 6402 value_true = graph()->NewNode( 6403 simplified()->ConvertTaggedHoleToUndefined(), value_true); 6404 } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) { 6405 // TODO(6587): avoid deopt if not all uses of value are truncated. 6406 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole; 6407 value_true = etrue = graph()->NewNode( 6408 simplified()->CheckFloat64Hole(mode, p.feedback()), value_true, 6409 etrue, if_true); 6410 } 6411 } 6412 6413 if (iteration_kind == IterationKind::kEntries) { 6414 // Allocate elements for key/value pair 6415 value_true = etrue = 6416 graph()->NewNode(javascript()->CreateKeyValueArray(), index, 6417 value_true, context, etrue); 6418 } else { 6419 DCHECK_EQ(IterationKind::kValues, iteration_kind); 6420 } 6421 } 6422 6423 // Increment the [[NextIndex]] field in the {iterator}. The TypeGuards 6424 // above guarantee that the {next_index} is in the UnsignedSmall range. 6425 Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index, 6426 jsgraph()->OneConstant()); 6427 etrue = graph()->NewNode(simplified()->StoreField(index_access), iterator, 6428 next_index, etrue, if_true); 6429 } 6430 6431 Node* done_false; 6432 Node* value_false; 6433 Node* efalse = effect; 6434 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 6435 { 6436 // iterator.[[NextIndex]] >= array.length, stop iterating. 6437 done_false = jsgraph()->TrueConstant(); 6438 value_false = jsgraph()->UndefinedConstant(); 6439 6440 if (!IsTypedArrayElementsKind(elements_kind)) { 6441 // Mark the {iterator} as exhausted by setting the [[NextIndex]] to a 6442 // value that will never pass the length check again (aka the maximum 6443 // value possible for the specific iterated object). Note that this is 6444 // different from what the specification says, which is changing the 6445 // [[IteratedObject]] field to undefined, but that makes it difficult 6446 // to eliminate the map checks and "length" accesses in for..of loops. 6447 // 6448 // This is not necessary for JSTypedArray's, since the length of those 6449 // cannot change later and so if we were ever out of bounds for them 6450 // we will stay out-of-bounds forever. 6451 Node* end_index = jsgraph()->Constant(index_access.type.Max()); 6452 efalse = graph()->NewNode(simplified()->StoreField(index_access), 6453 iterator, end_index, efalse, if_false); 6454 } 6455 } 6456 6457 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 6458 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 6459 Node* value = 6460 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 6461 value_true, value_false, control); 6462 Node* done = 6463 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 6464 done_true, done_false, control); 6465 6466 // Create IteratorResult object. 6467 value = effect = graph()->NewNode(javascript()->CreateIterResultObject(), 6468 value, done, context, effect); 6469 ReplaceWithValue(node, value, effect, control); 6470 return Replace(value); 6471} 6472 6473// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos ) 6474// ES6 section 21.1.3.3 String.prototype.codePointAt ( pos ) 6475Reduction JSCallReducer::ReduceStringPrototypeStringAt( 6476 const Operator* string_access_operator, Node* node) { 6477 DCHECK(string_access_operator->opcode() == IrOpcode::kStringCharCodeAt || 6478 string_access_operator->opcode() == IrOpcode::kStringCodePointAt); 6479 JSCallNode n(node); 6480 CallParameters const& p = n.Parameters(); 6481 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6482 return NoChange(); 6483 } 6484 6485 Node* receiver = n.receiver(); 6486 Node* index = n.ArgumentOr(0, jsgraph()->ZeroConstant()); 6487 Effect effect = n.effect(); 6488 Control control = n.control(); 6489 6490 // Ensure that the {receiver} is actually a String. 6491 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), 6492 receiver, effect, control); 6493 6494 // Determine the {receiver} length. 6495 Node* receiver_length = 6496 graph()->NewNode(simplified()->StringLength(), receiver); 6497 6498 // Check that the {index} is within range. 6499 index = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), 6500 index, receiver_length, effect, control); 6501 6502 // Return the character from the {receiver} as single character string. 6503 Node* value = effect = graph()->NewNode(string_access_operator, receiver, 6504 index, effect, control); 6505 6506 ReplaceWithValue(node, value, effect, control); 6507 return Replace(value); 6508} 6509 6510// ES section 21.1.3.20 6511// String.prototype.startsWith ( searchString [ , position ] ) 6512Reduction JSCallReducer::ReduceStringPrototypeStartsWith(Node* node) { 6513 JSCallNode n(node); 6514 CallParameters const& p = n.Parameters(); 6515 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6516 return NoChange(); 6517 } 6518 6519 TNode<Object> search_element = n.ArgumentOrUndefined(0, jsgraph()); 6520 6521 // Here are three conditions: 6522 // First, If search_element is definitely not a string, we make no change. 6523 // Second, If search_element is definitely a string and its length is less 6524 // or equal than max inline matching sequence threshold, we could inline 6525 // the entire matching sequence. 6526 // Third, we try to inline, and have a runtime deopt if search_element is 6527 // not a string. 6528 HeapObjectMatcher search_element_matcher(search_element); 6529 if (search_element_matcher.HasResolvedValue()) { 6530 ObjectRef target_ref = search_element_matcher.Ref(broker()); 6531 if (!target_ref.IsString()) return NoChange(); 6532 StringRef search_element_string = target_ref.AsString(); 6533 if (search_element_string.length().has_value()) { 6534 int length = search_element_string.length().value(); 6535 // If search_element's length is less or equal than 6536 // kMaxInlineMatchSequence, we inline the entire 6537 // matching sequence. 6538 if (length <= kMaxInlineMatchSequence) { 6539 JSCallReducerAssembler a(this, node); 6540 Node* subgraph = 6541 a.ReduceStringPrototypeStartsWith(search_element_string); 6542 return ReplaceWithSubgraph(&a, subgraph); 6543 } 6544 } 6545 } 6546 6547 JSCallReducerAssembler a(this, node); 6548 Node* subgraph = a.ReduceStringPrototypeStartsWith(); 6549 return ReplaceWithSubgraph(&a, subgraph); 6550} 6551 6552// ES section 21.1.3.1 String.prototype.charAt ( pos ) 6553Reduction JSCallReducer::ReduceStringPrototypeCharAt(Node* node) { 6554 JSCallNode n(node); 6555 CallParameters const& p = n.Parameters(); 6556 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6557 return NoChange(); 6558 } 6559 6560 Node* receiver = n.receiver(); 6561 Node* index = n.ArgumentOr(0, jsgraph()->ZeroConstant()); 6562 Effect effect = n.effect(); 6563 Control control = n.control(); 6564 6565 // Ensure that the {receiver} is actually a String. 6566 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), 6567 receiver, effect, control); 6568 6569 // Determine the {receiver} length. 6570 Node* receiver_length = 6571 graph()->NewNode(simplified()->StringLength(), receiver); 6572 6573 // Check that the {index} is within range. 6574 index = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), 6575 index, receiver_length, effect, control); 6576 6577 // Return the character from the {receiver} as single character string. 6578 Node* value = effect = graph()->NewNode(simplified()->StringCharCodeAt(), 6579 receiver, index, effect, control); 6580 value = graph()->NewNode(simplified()->StringFromSingleCharCode(), value); 6581 6582 ReplaceWithValue(node, value, effect, control); 6583 return Replace(value); 6584} 6585 6586#ifdef V8_INTL_SUPPORT 6587 6588Reduction JSCallReducer::ReduceStringPrototypeToLowerCaseIntl(Node* node) { 6589 JSCallNode n(node); 6590 CallParameters const& p = n.Parameters(); 6591 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6592 return NoChange(); 6593 } 6594 Effect effect = n.effect(); 6595 Control control = n.control(); 6596 6597 Node* receiver = effect = graph()->NewNode( 6598 simplified()->CheckString(p.feedback()), n.receiver(), effect, control); 6599 6600 NodeProperties::ReplaceEffectInput(node, effect); 6601 RelaxEffectsAndControls(node); 6602 node->ReplaceInput(0, receiver); 6603 node->TrimInputCount(1); 6604 NodeProperties::ChangeOp(node, simplified()->StringToLowerCaseIntl()); 6605 NodeProperties::SetType(node, Type::String()); 6606 return Changed(node); 6607} 6608 6609Reduction JSCallReducer::ReduceStringPrototypeToUpperCaseIntl(Node* node) { 6610 JSCallNode n(node); 6611 CallParameters const& p = n.Parameters(); 6612 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6613 return NoChange(); 6614 } 6615 Effect effect = n.effect(); 6616 Control control = n.control(); 6617 6618 Node* receiver = effect = graph()->NewNode( 6619 simplified()->CheckString(p.feedback()), n.receiver(), effect, control); 6620 6621 NodeProperties::ReplaceEffectInput(node, effect); 6622 RelaxEffectsAndControls(node); 6623 node->ReplaceInput(0, receiver); 6624 node->TrimInputCount(1); 6625 NodeProperties::ChangeOp(node, simplified()->StringToUpperCaseIntl()); 6626 NodeProperties::SetType(node, Type::String()); 6627 return Changed(node); 6628} 6629 6630#endif // V8_INTL_SUPPORT 6631 6632// ES #sec-string.fromcharcode 6633Reduction JSCallReducer::ReduceStringFromCharCode(Node* node) { 6634 JSCallNode n(node); 6635 CallParameters const& p = n.Parameters(); 6636 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6637 return NoChange(); 6638 } 6639 if (n.ArgumentCount() == 1) { 6640 Effect effect = n.effect(); 6641 Control control = n.control(); 6642 Node* input = n.Argument(0); 6643 6644 input = effect = graph()->NewNode( 6645 simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball, 6646 p.feedback()), 6647 input, effect, control); 6648 6649 Node* value = 6650 graph()->NewNode(simplified()->StringFromSingleCharCode(), input); 6651 ReplaceWithValue(node, value, effect); 6652 return Replace(value); 6653 } 6654 return NoChange(); 6655} 6656 6657// ES #sec-string.fromcodepoint 6658Reduction JSCallReducer::ReduceStringFromCodePoint(Node* node) { 6659 JSCallNode n(node); 6660 CallParameters const& p = n.Parameters(); 6661 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6662 return NoChange(); 6663 } 6664 if (n.ArgumentCount() != 1) return NoChange(); 6665 6666 Effect effect = n.effect(); 6667 Control control = n.control(); 6668 Node* input = n.Argument(0); 6669 6670 input = effect = graph()->NewNode( 6671 simplified()->CheckBounds(p.feedback(), 6672 CheckBoundsFlag::kConvertStringAndMinusZero), 6673 input, jsgraph()->Constant(0x10FFFF + 1), effect, control); 6674 6675 Node* value = 6676 graph()->NewNode(simplified()->StringFromSingleCodePoint(), input); 6677 ReplaceWithValue(node, value, effect); 6678 return Replace(value); 6679} 6680 6681Reduction JSCallReducer::ReduceStringPrototypeIterator(Node* node) { 6682 JSCallNode n(node); 6683 CallParameters const& p = n.Parameters(); 6684 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6685 return NoChange(); 6686 } 6687 Node* effect = NodeProperties::GetEffectInput(node); 6688 Node* control = NodeProperties::GetControlInput(node); 6689 Node* receiver = effect = graph()->NewNode( 6690 simplified()->CheckString(p.feedback()), n.receiver(), effect, control); 6691 Node* iterator = effect = 6692 graph()->NewNode(javascript()->CreateStringIterator(), receiver, 6693 jsgraph()->NoContextConstant(), effect); 6694 ReplaceWithValue(node, iterator, effect, control); 6695 return Replace(iterator); 6696} 6697 6698Reduction JSCallReducer::ReduceStringPrototypeLocaleCompare(Node* node) { 6699#ifdef V8_INTL_SUPPORT 6700 JSCallNode n(node); 6701 // Signature: receiver.localeCompare(compareString, locales, options) 6702 if (n.ArgumentCount() < 1 || n.ArgumentCount() > 3) { 6703 return NoChange(); 6704 } 6705 6706 { 6707 Handle<Object> locales; 6708 { 6709 HeapObjectMatcher m(n.ArgumentOrUndefined(1, jsgraph())); 6710 if (!m.HasResolvedValue()) return NoChange(); 6711 if (m.Is(factory()->undefined_value())) { 6712 locales = factory()->undefined_value(); 6713 } else { 6714 ObjectRef ref = m.Ref(broker()); 6715 if (!ref.IsString()) return NoChange(); 6716 StringRef sref = ref.AsString(); 6717 if (base::Optional<Handle<String>> maybe_locales = 6718 sref.ObjectIfContentAccessible()) { 6719 locales = *maybe_locales; 6720 } else { 6721 return NoChange(); 6722 } 6723 } 6724 } 6725 6726 TNode<Object> options = n.ArgumentOrUndefined(2, jsgraph()); 6727 { 6728 HeapObjectMatcher m(options); 6729 if (!m.Is(factory()->undefined_value())) { 6730 return NoChange(); 6731 } 6732 } 6733 6734 if (Intl::CompareStringsOptionsFor(broker()->local_isolate_or_isolate(), 6735 locales, factory()->undefined_value()) != 6736 Intl::CompareStringsOptions::kTryFastPath) { 6737 return NoChange(); 6738 } 6739 } 6740 6741 Callable callable = 6742 Builtins::CallableFor(isolate(), Builtin::kStringFastLocaleCompare); 6743 auto call_descriptor = Linkage::GetStubCallDescriptor( 6744 graph()->zone(), callable.descriptor(), 6745 callable.descriptor().GetStackParameterCount(), 6746 CallDescriptor::kNeedsFrameState); 6747 node->RemoveInput(n.FeedbackVectorIndex()); 6748 if (n.ArgumentCount() == 3) { 6749 node->RemoveInput(n.ArgumentIndex(2)); 6750 } else if (n.ArgumentCount() == 1) { 6751 node->InsertInput(graph()->zone(), n.LastArgumentIndex() + 1, 6752 jsgraph()->UndefinedConstant()); 6753 } else { 6754 DCHECK_EQ(2, n.ArgumentCount()); 6755 } 6756 node->InsertInput(graph()->zone(), 0, 6757 jsgraph()->HeapConstant(callable.code())); 6758 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); 6759 return Changed(node); 6760#else 6761 return NoChange(); 6762#endif 6763} 6764 6765Reduction JSCallReducer::ReduceStringIteratorPrototypeNext(Node* node) { 6766 JSCallNode n(node); 6767 Node* receiver = n.receiver(); 6768 Effect effect = n.effect(); 6769 Control control = n.control(); 6770 Node* context = n.context(); 6771 6772 MapInference inference(broker(), receiver, effect); 6773 if (!inference.HaveMaps() || 6774 !inference.AllOfInstanceTypesAre(JS_STRING_ITERATOR_TYPE)) { 6775 return NoChange(); 6776 } 6777 6778 Node* string = effect = graph()->NewNode( 6779 simplified()->LoadField(AccessBuilder::ForJSStringIteratorString()), 6780 receiver, effect, control); 6781 Node* index = effect = graph()->NewNode( 6782 simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()), 6783 receiver, effect, control); 6784 Node* length = graph()->NewNode(simplified()->StringLength(), string); 6785 6786 // branch0: if (index < length) 6787 Node* check0 = 6788 graph()->NewNode(simplified()->NumberLessThan(), index, length); 6789 Node* branch0 = 6790 graph()->NewNode(common()->Branch(BranchHint::kNone), check0, control); 6791 6792 Node* etrue0 = effect; 6793 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); 6794 Node* done_true; 6795 Node* vtrue0; 6796 { 6797 done_true = jsgraph()->FalseConstant(); 6798 vtrue0 = etrue0 = graph()->NewNode(simplified()->StringFromCodePointAt(), 6799 string, index, etrue0, if_true0); 6800 6801 // Update iterator.[[NextIndex]] 6802 Node* char_length = graph()->NewNode(simplified()->StringLength(), vtrue0); 6803 index = graph()->NewNode(simplified()->NumberAdd(), index, char_length); 6804 etrue0 = graph()->NewNode( 6805 simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()), 6806 receiver, index, etrue0, if_true0); 6807 } 6808 6809 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); 6810 Node* done_false; 6811 Node* vfalse0; 6812 { 6813 vfalse0 = jsgraph()->UndefinedConstant(); 6814 done_false = jsgraph()->TrueConstant(); 6815 } 6816 6817 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); 6818 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, effect, control); 6819 Node* value = 6820 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0, 6821 vfalse0, control); 6822 Node* done = 6823 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 6824 done_true, done_false, control); 6825 6826 value = effect = graph()->NewNode(javascript()->CreateIterResultObject(), 6827 value, done, context, effect); 6828 6829 ReplaceWithValue(node, value, effect, control); 6830 return Replace(value); 6831} 6832 6833// ES #sec-string.prototype.concat 6834Reduction JSCallReducer::ReduceStringPrototypeConcat(Node* node) { 6835 JSCallNode n(node); 6836 CallParameters const& p = n.Parameters(); 6837 const int parameter_count = n.ArgumentCount(); 6838 if (parameter_count > 1) return NoChange(); 6839 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6840 return NoChange(); 6841 } 6842 6843 Effect effect = n.effect(); 6844 Control control = n.control(); 6845 Node* receiver = effect = graph()->NewNode( 6846 simplified()->CheckString(p.feedback()), n.receiver(), effect, control); 6847 6848 if (parameter_count == 0) { 6849 ReplaceWithValue(node, receiver, effect, control); 6850 return Replace(receiver); 6851 } 6852 6853 Node* argument = effect = graph()->NewNode( 6854 simplified()->CheckString(p.feedback()), n.Argument(0), effect, control); 6855 Node* receiver_length = 6856 graph()->NewNode(simplified()->StringLength(), receiver); 6857 Node* argument_length = 6858 graph()->NewNode(simplified()->StringLength(), argument); 6859 Node* length = graph()->NewNode(simplified()->NumberAdd(), receiver_length, 6860 argument_length); 6861 length = effect = graph()->NewNode( 6862 simplified()->CheckBounds(p.feedback()), length, 6863 jsgraph()->Constant(String::kMaxLength + 1), effect, control); 6864 6865 Node* value = graph()->NewNode(simplified()->StringConcat(), length, receiver, 6866 argument); 6867 6868 ReplaceWithValue(node, value, effect, control); 6869 return Replace(value); 6870} 6871 6872Reduction JSCallReducer::ReducePromiseConstructor(Node* node) { 6873 PromiseBuiltinReducerAssembler a(this, node, broker()); 6874 6875 // We only inline when we have the executor. 6876 if (a.ConstructArity() < 1) return NoChange(); 6877 // Only handle builtins Promises, not subclasses. 6878 if (a.TargetInput() != a.NewTargetInput()) return NoChange(); 6879 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange(); 6880 6881 TNode<Object> subgraph = a.ReducePromiseConstructor(native_context()); 6882 return ReplaceWithSubgraph(&a, subgraph); 6883} 6884 6885bool JSCallReducer::DoPromiseChecks(MapInference* inference) { 6886 if (!inference->HaveMaps()) return false; 6887 ZoneVector<MapRef> const& receiver_maps = inference->GetMaps(); 6888 6889 // Check whether all {receiver_maps} are JSPromise maps and 6890 // have the initial Promise.prototype as their [[Prototype]]. 6891 for (const MapRef& receiver_map : receiver_maps) { 6892 if (!receiver_map.IsJSPromiseMap()) return false; 6893 HeapObjectRef prototype = receiver_map.prototype(); 6894 if (!prototype.equals(native_context().promise_prototype())) { 6895 return false; 6896 } 6897 } 6898 6899 return true; 6900} 6901 6902// ES section #sec-promise.prototype.catch 6903Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) { 6904 JSCallNode n(node); 6905 CallParameters const& p = n.Parameters(); 6906 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6907 return NoChange(); 6908 } 6909 int arity = p.arity_without_implicit_args(); 6910 Node* receiver = n.receiver(); 6911 Effect effect = n.effect(); 6912 Control control = n.control(); 6913 6914 MapInference inference(broker(), receiver, effect); 6915 if (!DoPromiseChecks(&inference)) return inference.NoChange(); 6916 6917 if (!dependencies()->DependOnPromiseThenProtector()) { 6918 return inference.NoChange(); 6919 } 6920 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 6921 control, p.feedback()); 6922 6923 // Massage the {node} to call "then" instead by first removing all inputs 6924 // following the onRejected parameter, and then filling up the parameters 6925 // to two inputs from the left with undefined. 6926 Node* target = jsgraph()->Constant(native_context().promise_then()); 6927 NodeProperties::ReplaceValueInput(node, target, 0); 6928 NodeProperties::ReplaceEffectInput(node, effect); 6929 for (; arity > 1; --arity) node->RemoveInput(3); 6930 for (; arity < 2; ++arity) { 6931 node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant()); 6932 } 6933 NodeProperties::ChangeOp( 6934 node, javascript()->Call( 6935 JSCallNode::ArityForArgc(arity), p.frequency(), p.feedback(), 6936 ConvertReceiverMode::kNotNullOrUndefined, p.speculation_mode(), 6937 CallFeedbackRelation::kUnrelated)); 6938 return Changed(node).FollowedBy(ReducePromisePrototypeThen(node)); 6939} 6940 6941Node* JSCallReducer::CreateClosureFromBuiltinSharedFunctionInfo( 6942 SharedFunctionInfoRef shared, Node* context, Node* effect, Node* control) { 6943 DCHECK(shared.HasBuiltinId()); 6944 Handle<FeedbackCell> feedback_cell = 6945 isolate()->factory()->many_closures_cell(); 6946 Callable const callable = 6947 Builtins::CallableFor(isolate(), shared.builtin_id()); 6948 CodeTRef code = MakeRef(broker(), *callable.code()); 6949 return graph()->NewNode(javascript()->CreateClosure(shared, code), 6950 jsgraph()->HeapConstant(feedback_cell), context, 6951 effect, control); 6952} 6953 6954// ES section #sec-promise.prototype.finally 6955Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) { 6956 JSCallNode n(node); 6957 CallParameters const& p = n.Parameters(); 6958 int arity = p.arity_without_implicit_args(); 6959 Node* receiver = n.receiver(); 6960 Node* on_finally = n.ArgumentOrUndefined(0, jsgraph()); 6961 Effect effect = n.effect(); 6962 Control control = n.control(); 6963 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 6964 return NoChange(); 6965 } 6966 6967 MapInference inference(broker(), receiver, effect); 6968 if (!DoPromiseChecks(&inference)) return inference.NoChange(); 6969 ZoneVector<MapRef> const& receiver_maps = inference.GetMaps(); 6970 6971 if (!dependencies()->DependOnPromiseHookProtector()) { 6972 return inference.NoChange(); 6973 } 6974 if (!dependencies()->DependOnPromiseThenProtector()) { 6975 return inference.NoChange(); 6976 } 6977 if (!dependencies()->DependOnPromiseSpeciesProtector()) { 6978 return inference.NoChange(); 6979 } 6980 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 6981 control, p.feedback()); 6982 6983 // Check if {on_finally} is callable, and if so wrap it into appropriate 6984 // closures that perform the finalization. 6985 Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), on_finally); 6986 Node* branch = 6987 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 6988 6989 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 6990 Node* etrue = effect; 6991 Node* catch_true; 6992 Node* then_true; 6993 { 6994 Node* context = jsgraph()->Constant(native_context()); 6995 Node* constructor = 6996 jsgraph()->Constant(native_context().promise_function()); 6997 6998 // Allocate shared context for the closures below. 6999 context = etrue = 7000 graph()->NewNode(javascript()->CreateFunctionContext( 7001 native_context().scope_info(), 7002 PromiseBuiltins::kPromiseFinallyContextLength - 7003 Context::MIN_CONTEXT_SLOTS, 7004 FUNCTION_SCOPE), 7005 context, etrue, if_true); 7006 etrue = graph()->NewNode( 7007 simplified()->StoreField( 7008 AccessBuilder::ForContextSlot(PromiseBuiltins::kOnFinallySlot)), 7009 context, on_finally, etrue, if_true); 7010 etrue = graph()->NewNode( 7011 simplified()->StoreField( 7012 AccessBuilder::ForContextSlot(PromiseBuiltins::kConstructorSlot)), 7013 context, constructor, etrue, if_true); 7014 7015 // Allocate the closure for the reject case. 7016 SharedFunctionInfoRef promise_catch_finally = 7017 MakeRef(broker(), factory()->promise_catch_finally_shared_fun()); 7018 catch_true = etrue = CreateClosureFromBuiltinSharedFunctionInfo( 7019 promise_catch_finally, context, etrue, if_true); 7020 7021 // Allocate the closure for the fulfill case. 7022 SharedFunctionInfoRef promise_then_finally = 7023 MakeRef(broker(), factory()->promise_then_finally_shared_fun()); 7024 then_true = etrue = CreateClosureFromBuiltinSharedFunctionInfo( 7025 promise_then_finally, context, etrue, if_true); 7026 } 7027 7028 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 7029 Node* efalse = effect; 7030 Node* catch_false = on_finally; 7031 Node* then_false = on_finally; 7032 7033 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 7034 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 7035 Node* catch_finally = 7036 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 7037 catch_true, catch_false, control); 7038 Node* then_finally = 7039 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 7040 then_true, then_false, control); 7041 7042 // At this point we definitely know that {receiver} has one of the 7043 // {receiver_maps}, so insert a MapGuard as a hint for the lowering 7044 // of the call to "then" below. 7045 { 7046 ZoneHandleSet<Map> maps; 7047 for (const MapRef& map : receiver_maps) { 7048 maps.insert(map.object(), graph()->zone()); 7049 } 7050 effect = graph()->NewNode(simplified()->MapGuard(maps), receiver, effect, 7051 control); 7052 } 7053 7054 // Massage the {node} to call "then" instead by first removing all inputs 7055 // following the onFinally parameter, and then replacing the only parameter 7056 // input with the {on_finally} value. 7057 Node* target = jsgraph()->Constant(native_context().promise_then()); 7058 NodeProperties::ReplaceValueInput(node, target, n.TargetIndex()); 7059 NodeProperties::ReplaceEffectInput(node, effect); 7060 NodeProperties::ReplaceControlInput(node, control); 7061 for (; arity > 2; --arity) node->RemoveInput(2); 7062 for (; arity < 2; ++arity) { 7063 node->InsertInput(graph()->zone(), 2, then_finally); 7064 } 7065 node->ReplaceInput(2, then_finally); 7066 node->ReplaceInput(3, catch_finally); 7067 NodeProperties::ChangeOp( 7068 node, javascript()->Call( 7069 JSCallNode::ArityForArgc(arity), p.frequency(), p.feedback(), 7070 ConvertReceiverMode::kNotNullOrUndefined, p.speculation_mode(), 7071 CallFeedbackRelation::kUnrelated)); 7072 return Changed(node).FollowedBy(ReducePromisePrototypeThen(node)); 7073} 7074 7075Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { 7076 JSCallNode n(node); 7077 CallParameters const& p = n.Parameters(); 7078 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 7079 return NoChange(); 7080 } 7081 7082 Node* receiver = n.receiver(); 7083 Node* on_fulfilled = n.ArgumentOrUndefined(0, jsgraph()); 7084 Node* on_rejected = n.ArgumentOrUndefined(1, jsgraph()); 7085 Node* context = n.context(); 7086 Effect effect = n.effect(); 7087 Control control = n.control(); 7088 FrameState frame_state = n.frame_state(); 7089 7090 MapInference inference(broker(), receiver, effect); 7091 if (!DoPromiseChecks(&inference)) return inference.NoChange(); 7092 7093 if (!dependencies()->DependOnPromiseHookProtector()) { 7094 return inference.NoChange(); 7095 } 7096 if (!dependencies()->DependOnPromiseSpeciesProtector()) { 7097 return inference.NoChange(); 7098 } 7099 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 7100 control, p.feedback()); 7101 7102 // Check that {on_fulfilled} is callable. 7103 on_fulfilled = graph()->NewNode( 7104 common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue), 7105 graph()->NewNode(simplified()->ObjectIsCallable(), on_fulfilled), 7106 on_fulfilled, jsgraph()->UndefinedConstant()); 7107 7108 // Check that {on_rejected} is callable. 7109 on_rejected = graph()->NewNode( 7110 common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue), 7111 graph()->NewNode(simplified()->ObjectIsCallable(), on_rejected), 7112 on_rejected, jsgraph()->UndefinedConstant()); 7113 7114 // Create the resulting JSPromise. 7115 Node* promise = effect = 7116 graph()->NewNode(javascript()->CreatePromise(), context, effect); 7117 7118 // Chain {result} onto {receiver}. 7119 promise = effect = graph()->NewNode( 7120 javascript()->PerformPromiseThen(), receiver, on_fulfilled, on_rejected, 7121 promise, context, frame_state, effect, control); 7122 7123 // At this point we know that {promise} is going to have the 7124 // initial Promise map, since even if {PerformPromiseThen} 7125 // above called into the host rejection tracker, the {promise} 7126 // doesn't escape to user JavaScript. So bake this information 7127 // into the graph such that subsequent passes can use the 7128 // information for further optimizations. 7129 MapRef promise_map = 7130 native_context().promise_function().initial_map(dependencies()); 7131 effect = graph()->NewNode( 7132 simplified()->MapGuard(ZoneHandleSet<Map>(promise_map.object())), promise, 7133 effect, control); 7134 7135 ReplaceWithValue(node, promise, effect, control); 7136 return Replace(promise); 7137} 7138 7139// ES section #sec-promise.resolve 7140Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) { 7141 JSCallNode n(node); 7142 Node* receiver = n.receiver(); 7143 Node* value = n.ArgumentOrUndefined(0, jsgraph()); 7144 Node* context = n.context(); 7145 Effect effect = n.effect(); 7146 Control control = n.control(); 7147 FrameState frame_state = n.frame_state(); 7148 7149 // Only reduce when the receiver is guaranteed to be a JSReceiver. 7150 MapInference inference(broker(), receiver, effect); 7151 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAreJSReceiver()) { 7152 return NoChange(); 7153 } 7154 7155 // Morph the {node} into a JSPromiseResolve operation. 7156 node->ReplaceInput(0, receiver); 7157 node->ReplaceInput(1, value); 7158 node->ReplaceInput(2, context); 7159 node->ReplaceInput(3, frame_state); 7160 node->ReplaceInput(4, effect); 7161 node->ReplaceInput(5, control); 7162 node->TrimInputCount(6); 7163 NodeProperties::ChangeOp(node, javascript()->PromiseResolve()); 7164 return Changed(node); 7165} 7166 7167// ES #sec-typedarray-constructors 7168Reduction JSCallReducer::ReduceTypedArrayConstructor( 7169 Node* node, const SharedFunctionInfoRef& shared) { 7170 JSConstructNode n(node); 7171 ConstructParameters const& p = n.Parameters(); 7172 int arity = p.arity_without_implicit_args(); 7173 Node* target = n.target(); 7174 Node* arg0 = n.ArgumentOrUndefined(0, jsgraph()); 7175 Node* arg1 = n.ArgumentOrUndefined(1, jsgraph()); 7176 Node* arg2 = n.ArgumentOrUndefined(2, jsgraph()); 7177 Node* new_target = n.new_target(); 7178 Node* context = n.context(); 7179 FrameState frame_state = n.frame_state(); 7180 Effect effect = n.effect(); 7181 Control control = n.control(); 7182 7183 // Insert a construct stub frame into the chain of frame states. This will 7184 // reconstruct the proper frame when deoptimizing within the constructor. 7185 frame_state = CreateArtificialFrameState( 7186 node, frame_state, arity, BytecodeOffset::ConstructStubInvoke(), 7187 FrameStateType::kConstructStub, shared, context, common(), graph()); 7188 7189 // This continuation just returns the newly created JSTypedArray. We 7190 // pass the_hole as the receiver, just like the builtin construct stub 7191 // does in this case. 7192 Node* const parameters[] = {jsgraph()->TheHoleConstant()}; 7193 int const num_parameters = static_cast<int>(arraysize(parameters)); 7194 frame_state = CreateJavaScriptBuiltinContinuationFrameState( 7195 jsgraph(), shared, Builtin::kGenericLazyDeoptContinuation, target, 7196 context, parameters, num_parameters, frame_state, 7197 ContinuationFrameStateMode::LAZY); 7198 7199 Node* result = 7200 graph()->NewNode(javascript()->CreateTypedArray(), target, new_target, 7201 arg0, arg1, arg2, context, frame_state, effect, control); 7202 return Replace(result); 7203} 7204 7205// ES #sec-get-%typedarray%.prototype-@@tostringtag 7206Reduction JSCallReducer::ReduceTypedArrayPrototypeToStringTag(Node* node) { 7207 Node* receiver = NodeProperties::GetValueInput(node, 1); 7208 Node* effect = NodeProperties::GetEffectInput(node); 7209 Node* control = NodeProperties::GetControlInput(node); 7210 7211 NodeVector values(graph()->zone()); 7212 NodeVector effects(graph()->zone()); 7213 NodeVector controls(graph()->zone()); 7214 7215 Node* smi_check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); 7216 control = graph()->NewNode(common()->Branch(BranchHint::kFalse), smi_check, 7217 control); 7218 7219 values.push_back(jsgraph()->UndefinedConstant()); 7220 effects.push_back(effect); 7221 controls.push_back(graph()->NewNode(common()->IfTrue(), control)); 7222 7223 control = graph()->NewNode(common()->IfFalse(), control); 7224 Node* receiver_map = effect = 7225 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), 7226 receiver, effect, control); 7227 Node* receiver_bit_field2 = effect = graph()->NewNode( 7228 simplified()->LoadField(AccessBuilder::ForMapBitField2()), receiver_map, 7229 effect, control); 7230 Node* receiver_elements_kind = graph()->NewNode( 7231 simplified()->NumberShiftRightLogical(), 7232 graph()->NewNode( 7233 simplified()->NumberBitwiseAnd(), receiver_bit_field2, 7234 jsgraph()->Constant(Map::Bits2::ElementsKindBits::kMask)), 7235 jsgraph()->Constant(Map::Bits2::ElementsKindBits::kShift)); 7236 7237 // Offset the elements kind by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, 7238 // so that the branch cascade below is turned into a simple table 7239 // switch by the ControlFlowOptimizer later. 7240 receiver_elements_kind = graph()->NewNode( 7241 simplified()->NumberSubtract(), receiver_elements_kind, 7242 jsgraph()->Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); 7243 7244#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ 7245 do { \ 7246 Node* check = graph()->NewNode( \ 7247 simplified()->NumberEqual(), receiver_elements_kind, \ 7248 jsgraph()->Constant(TYPE##_ELEMENTS - \ 7249 FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); \ 7250 control = graph()->NewNode(common()->Branch(), check, control); \ 7251 values.push_back(jsgraph()->Constant( \ 7252 broker()->GetTypedArrayStringTag(TYPE##_ELEMENTS))); \ 7253 effects.push_back(effect); \ 7254 controls.push_back(graph()->NewNode(common()->IfTrue(), control)); \ 7255 control = graph()->NewNode(common()->IfFalse(), control); \ 7256 } while (false); 7257 TYPED_ARRAYS(TYPED_ARRAY_CASE) 7258#undef TYPED_ARRAY_CASE 7259 7260 values.push_back(jsgraph()->UndefinedConstant()); 7261 effects.push_back(effect); 7262 controls.push_back(control); 7263 7264 int const count = static_cast<int>(controls.size()); 7265 control = graph()->NewNode(common()->Merge(count), count, &controls.front()); 7266 effects.push_back(control); 7267 effect = 7268 graph()->NewNode(common()->EffectPhi(count), count + 1, &effects.front()); 7269 values.push_back(control); 7270 Node* value = 7271 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count), 7272 count + 1, &values.front()); 7273 ReplaceWithValue(node, value, effect, control); 7274 return Replace(value); 7275} 7276 7277// ES #sec-number.isfinite 7278Reduction JSCallReducer::ReduceNumberIsFinite(Node* node) { 7279 JSCallNode n(node); 7280 if (n.ArgumentCount() < 1) { 7281 Node* value = jsgraph()->FalseConstant(); 7282 ReplaceWithValue(node, value); 7283 return Replace(value); 7284 } 7285 Node* input = n.Argument(0); 7286 Node* value = graph()->NewNode(simplified()->ObjectIsFiniteNumber(), input); 7287 ReplaceWithValue(node, value); 7288 return Replace(value); 7289} 7290 7291// ES #sec-number.isfinite 7292Reduction JSCallReducer::ReduceNumberIsInteger(Node* node) { 7293 JSCallNode n(node); 7294 if (n.ArgumentCount() < 1) { 7295 Node* value = jsgraph()->FalseConstant(); 7296 ReplaceWithValue(node, value); 7297 return Replace(value); 7298 } 7299 Node* input = n.Argument(0); 7300 Node* value = graph()->NewNode(simplified()->ObjectIsInteger(), input); 7301 ReplaceWithValue(node, value); 7302 return Replace(value); 7303} 7304 7305// ES #sec-number.issafeinteger 7306Reduction JSCallReducer::ReduceNumberIsSafeInteger(Node* node) { 7307 JSCallNode n(node); 7308 if (n.ArgumentCount() < 1) { 7309 Node* value = jsgraph()->FalseConstant(); 7310 ReplaceWithValue(node, value); 7311 return Replace(value); 7312 } 7313 Node* input = n.Argument(0); 7314 Node* value = graph()->NewNode(simplified()->ObjectIsSafeInteger(), input); 7315 ReplaceWithValue(node, value); 7316 return Replace(value); 7317} 7318 7319// ES #sec-number.isnan 7320Reduction JSCallReducer::ReduceNumberIsNaN(Node* node) { 7321 JSCallNode n(node); 7322 if (n.ArgumentCount() < 1) { 7323 Node* value = jsgraph()->FalseConstant(); 7324 ReplaceWithValue(node, value); 7325 return Replace(value); 7326 } 7327 Node* input = n.Argument(0); 7328 Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input); 7329 ReplaceWithValue(node, value); 7330 return Replace(value); 7331} 7332 7333Reduction JSCallReducer::ReduceMapPrototypeGet(Node* node) { 7334 // We only optimize if we have target, receiver and key parameters. 7335 JSCallNode n(node); 7336 if (n.ArgumentCount() != 1) return NoChange(); 7337 Node* receiver = NodeProperties::GetValueInput(node, 1); 7338 Effect effect{NodeProperties::GetEffectInput(node)}; 7339 Control control{NodeProperties::GetControlInput(node)}; 7340 Node* key = NodeProperties::GetValueInput(node, 2); 7341 7342 MapInference inference(broker(), receiver, effect); 7343 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAre(JS_MAP_TYPE)) { 7344 return NoChange(); 7345 } 7346 7347 Node* table = effect = graph()->NewNode( 7348 simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, 7349 effect, control); 7350 7351 Node* entry = effect = graph()->NewNode( 7352 simplified()->FindOrderedHashMapEntry(), table, key, effect, control); 7353 7354 Node* check = graph()->NewNode(simplified()->NumberEqual(), entry, 7355 jsgraph()->MinusOneConstant()); 7356 7357 Node* branch = graph()->NewNode(common()->Branch(), check, control); 7358 7359 // Key not found. 7360 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 7361 Node* etrue = effect; 7362 Node* vtrue = jsgraph()->UndefinedConstant(); 7363 7364 // Key found. 7365 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 7366 Node* efalse = effect; 7367 Node* vfalse = efalse = graph()->NewNode( 7368 simplified()->LoadElement(AccessBuilder::ForOrderedHashMapEntryValue()), 7369 table, entry, efalse, if_false); 7370 7371 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 7372 Node* value = graph()->NewNode( 7373 common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control); 7374 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 7375 7376 ReplaceWithValue(node, value, effect, control); 7377 return Replace(value); 7378} 7379 7380Reduction JSCallReducer::ReduceMapPrototypeHas(Node* node) { 7381 // We only optimize if we have target, receiver and key parameters. 7382 JSCallNode n(node); 7383 if (n.ArgumentCount() != 1) return NoChange(); 7384 Node* receiver = NodeProperties::GetValueInput(node, 1); 7385 Effect effect{NodeProperties::GetEffectInput(node)}; 7386 Control control{NodeProperties::GetControlInput(node)}; 7387 Node* key = NodeProperties::GetValueInput(node, 2); 7388 7389 MapInference inference(broker(), receiver, effect); 7390 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAre(JS_MAP_TYPE)) { 7391 return NoChange(); 7392 } 7393 7394 Node* table = effect = graph()->NewNode( 7395 simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, 7396 effect, control); 7397 7398 Node* index = effect = graph()->NewNode( 7399 simplified()->FindOrderedHashMapEntry(), table, key, effect, control); 7400 7401 Node* value = graph()->NewNode(simplified()->NumberEqual(), index, 7402 jsgraph()->MinusOneConstant()); 7403 value = graph()->NewNode(simplified()->BooleanNot(), value); 7404 7405 ReplaceWithValue(node, value, effect, control); 7406 return Replace(value); 7407} 7408 7409namespace { 7410 7411InstanceType InstanceTypeForCollectionKind(CollectionKind kind) { 7412 switch (kind) { 7413 case CollectionKind::kMap: 7414 return JS_MAP_TYPE; 7415 case CollectionKind::kSet: 7416 return JS_SET_TYPE; 7417 } 7418 UNREACHABLE(); 7419} 7420 7421} // namespace 7422 7423Reduction JSCallReducer::ReduceCollectionIteration( 7424 Node* node, CollectionKind collection_kind, IterationKind iteration_kind) { 7425 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 7426 Node* receiver = NodeProperties::GetValueInput(node, 1); 7427 Node* context = NodeProperties::GetContextInput(node); 7428 Effect effect{NodeProperties::GetEffectInput(node)}; 7429 Control control{NodeProperties::GetControlInput(node)}; 7430 7431 InstanceType type = InstanceTypeForCollectionKind(collection_kind); 7432 MapInference inference(broker(), receiver, effect); 7433 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAre(type)) { 7434 return NoChange(); 7435 } 7436 7437 Node* js_create_iterator = effect = graph()->NewNode( 7438 javascript()->CreateCollectionIterator(collection_kind, iteration_kind), 7439 receiver, context, effect, control); 7440 ReplaceWithValue(node, js_create_iterator, effect); 7441 return Replace(js_create_iterator); 7442} 7443 7444Reduction JSCallReducer::ReduceCollectionPrototypeSize( 7445 Node* node, CollectionKind collection_kind) { 7446 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); 7447 Node* receiver = NodeProperties::GetValueInput(node, 1); 7448 Effect effect{NodeProperties::GetEffectInput(node)}; 7449 Control control{NodeProperties::GetControlInput(node)}; 7450 7451 InstanceType type = InstanceTypeForCollectionKind(collection_kind); 7452 MapInference inference(broker(), receiver, effect); 7453 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAre(type)) { 7454 return NoChange(); 7455 } 7456 7457 Node* table = effect = graph()->NewNode( 7458 simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, 7459 effect, control); 7460 Node* value = effect = graph()->NewNode( 7461 simplified()->LoadField( 7462 AccessBuilder::ForOrderedHashMapOrSetNumberOfElements()), 7463 table, effect, control); 7464 ReplaceWithValue(node, value, effect, control); 7465 return Replace(value); 7466} 7467 7468Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext( 7469 Node* node, int entry_size, Handle<HeapObject> empty_collection, 7470 InstanceType collection_iterator_instance_type_first, 7471 InstanceType collection_iterator_instance_type_last) { 7472 JSCallNode n(node); 7473 CallParameters const& p = n.Parameters(); 7474 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 7475 return NoChange(); 7476 } 7477 7478 Node* receiver = n.receiver(); 7479 Node* context = n.context(); 7480 Effect effect = n.effect(); 7481 Control control = n.control(); 7482 7483 // A word of warning to begin with: This whole method might look a bit 7484 // strange at times, but that's mostly because it was carefully handcrafted 7485 // to allow for full escape analysis and scalar replacement of both the 7486 // collection iterator object and the iterator results, including the 7487 // key-value arrays in case of Set/Map entry iteration. 7488 // 7489 // TODO(turbofan): Currently the escape analysis (and the store-load 7490 // forwarding) is unable to eliminate the allocations for the key-value 7491 // arrays in case of Set/Map entry iteration, and we should investigate 7492 // how to update the escape analysis / arrange the graph in a way that 7493 // this becomes possible. 7494 7495 InstanceType receiver_instance_type; 7496 { 7497 MapInference inference(broker(), receiver, effect); 7498 if (!inference.HaveMaps()) return NoChange(); 7499 ZoneVector<MapRef> const& receiver_maps = inference.GetMaps(); 7500 receiver_instance_type = receiver_maps[0].instance_type(); 7501 for (size_t i = 1; i < receiver_maps.size(); ++i) { 7502 if (receiver_maps[i].instance_type() != receiver_instance_type) { 7503 return inference.NoChange(); 7504 } 7505 } 7506 if (receiver_instance_type < collection_iterator_instance_type_first || 7507 receiver_instance_type > collection_iterator_instance_type_last) { 7508 return inference.NoChange(); 7509 } 7510 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 7511 control, p.feedback()); 7512 } 7513 7514 // Transition the JSCollectionIterator {receiver} if necessary 7515 // (i.e. there were certain mutations while we're iterating). 7516 { 7517 Node* done_loop; 7518 Node* done_eloop; 7519 Node* loop = control = 7520 graph()->NewNode(common()->Loop(2), control, control); 7521 Node* eloop = effect = 7522 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); 7523 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); 7524 NodeProperties::MergeControlToEnd(graph(), common(), terminate); 7525 7526 // Check if reached the final table of the {receiver}. 7527 Node* table = effect = graph()->NewNode( 7528 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()), 7529 receiver, effect, control); 7530 Node* next_table = effect = 7531 graph()->NewNode(simplified()->LoadField( 7532 AccessBuilder::ForOrderedHashMapOrSetNextTable()), 7533 table, effect, control); 7534 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table); 7535 control = 7536 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); 7537 7538 // Abort the {loop} when we reach the final table. 7539 done_loop = graph()->NewNode(common()->IfTrue(), control); 7540 done_eloop = effect; 7541 7542 // Migrate to the {next_table} otherwise. 7543 control = graph()->NewNode(common()->IfFalse(), control); 7544 7545 // Self-heal the {receiver}s index. 7546 Node* index = effect = graph()->NewNode( 7547 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()), 7548 receiver, effect, control); 7549 Callable const callable = 7550 Builtins::CallableFor(isolate(), Builtin::kOrderedHashTableHealIndex); 7551 auto call_descriptor = Linkage::GetStubCallDescriptor( 7552 graph()->zone(), callable.descriptor(), 7553 callable.descriptor().GetStackParameterCount(), 7554 CallDescriptor::kNoFlags, Operator::kEliminatable); 7555 index = effect = 7556 graph()->NewNode(common()->Call(call_descriptor), 7557 jsgraph()->HeapConstant(callable.code()), table, index, 7558 jsgraph()->NoContextConstant(), effect); 7559 7560 index = effect = graph()->NewNode( 7561 common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), index, 7562 effect, control); 7563 7564 // Update the {index} and {table} on the {receiver}. 7565 effect = graph()->NewNode( 7566 simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorIndex()), 7567 receiver, index, effect, control); 7568 effect = graph()->NewNode( 7569 simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorTable()), 7570 receiver, next_table, effect, control); 7571 7572 // Tie the knot. 7573 loop->ReplaceInput(1, control); 7574 eloop->ReplaceInput(1, effect); 7575 7576 control = done_loop; 7577 effect = done_eloop; 7578 } 7579 7580 // Get current index and table from the JSCollectionIterator {receiver}. 7581 Node* index = effect = graph()->NewNode( 7582 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()), 7583 receiver, effect, control); 7584 Node* table = effect = graph()->NewNode( 7585 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()), 7586 receiver, effect, control); 7587 7588 // Create the {JSIteratorResult} first to ensure that we always have 7589 // a dominating Allocate node for the allocation folding phase. 7590 Node* iterator_result = effect = graph()->NewNode( 7591 javascript()->CreateIterResultObject(), jsgraph()->UndefinedConstant(), 7592 jsgraph()->TrueConstant(), context, effect); 7593 7594 // Look for the next non-holey key, starting from {index} in the {table}. 7595 Node* controls[2]; 7596 Node* effects[3]; 7597 { 7598 // Compute the currently used capacity. 7599 Node* number_of_buckets = effect = graph()->NewNode( 7600 simplified()->LoadField( 7601 AccessBuilder::ForOrderedHashMapOrSetNumberOfBuckets()), 7602 table, effect, control); 7603 Node* number_of_elements = effect = graph()->NewNode( 7604 simplified()->LoadField( 7605 AccessBuilder::ForOrderedHashMapOrSetNumberOfElements()), 7606 table, effect, control); 7607 Node* number_of_deleted_elements = effect = graph()->NewNode( 7608 simplified()->LoadField( 7609 AccessBuilder::ForOrderedHashMapOrSetNumberOfDeletedElements()), 7610 table, effect, control); 7611 Node* used_capacity = 7612 graph()->NewNode(simplified()->NumberAdd(), number_of_elements, 7613 number_of_deleted_elements); 7614 7615 // Skip holes and update the {index}. 7616 Node* loop = graph()->NewNode(common()->Loop(2), control, control); 7617 Node* eloop = 7618 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); 7619 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); 7620 NodeProperties::MergeControlToEnd(graph(), common(), terminate); 7621 Node* iloop = graph()->NewNode( 7622 common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop); 7623 7624 index = effect = graph()->NewNode( 7625 common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), iloop, 7626 eloop, control); 7627 { 7628 Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), index, 7629 used_capacity); 7630 Node* branch0 = 7631 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, loop); 7632 7633 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); 7634 Node* efalse0 = effect; 7635 { 7636 // Mark the {receiver} as exhausted. 7637 efalse0 = graph()->NewNode( 7638 simplified()->StoreField( 7639 AccessBuilder::ForJSCollectionIteratorTable()), 7640 receiver, jsgraph()->HeapConstant(empty_collection), efalse0, 7641 if_false0); 7642 7643 controls[0] = if_false0; 7644 effects[0] = efalse0; 7645 } 7646 7647 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); 7648 Node* etrue0 = effect; 7649 { 7650 // Load the key of the entry. 7651 STATIC_ASSERT(OrderedHashMap::HashTableStartIndex() == 7652 OrderedHashSet::HashTableStartIndex()); 7653 Node* entry_start_position = graph()->NewNode( 7654 simplified()->NumberAdd(), 7655 graph()->NewNode( 7656 simplified()->NumberAdd(), 7657 graph()->NewNode(simplified()->NumberMultiply(), index, 7658 jsgraph()->Constant(entry_size)), 7659 number_of_buckets), 7660 jsgraph()->Constant(OrderedHashMap::HashTableStartIndex())); 7661 Node* entry_key = etrue0 = graph()->NewNode( 7662 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()), 7663 table, entry_start_position, etrue0, if_true0); 7664 7665 // Advance the index. 7666 index = graph()->NewNode(simplified()->NumberAdd(), index, 7667 jsgraph()->OneConstant()); 7668 7669 Node* check1 = 7670 graph()->NewNode(simplified()->ReferenceEqual(), entry_key, 7671 jsgraph()->TheHoleConstant()); 7672 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), 7673 check1, if_true0); 7674 7675 { 7676 // Abort loop with resulting value. 7677 control = graph()->NewNode(common()->IfFalse(), branch1); 7678 effect = etrue0; 7679 Node* value = effect = 7680 graph()->NewNode(common()->TypeGuard(Type::NonInternal()), 7681 entry_key, effect, control); 7682 Node* done = jsgraph()->FalseConstant(); 7683 7684 // Advance the index on the {receiver}. 7685 effect = graph()->NewNode( 7686 simplified()->StoreField( 7687 AccessBuilder::ForJSCollectionIteratorIndex()), 7688 receiver, index, effect, control); 7689 7690 // The actual {value} depends on the {receiver} iteration type. 7691 switch (receiver_instance_type) { 7692 case JS_MAP_KEY_ITERATOR_TYPE: 7693 case JS_SET_VALUE_ITERATOR_TYPE: 7694 break; 7695 7696 case JS_SET_KEY_VALUE_ITERATOR_TYPE: 7697 value = effect = 7698 graph()->NewNode(javascript()->CreateKeyValueArray(), value, 7699 value, context, effect); 7700 break; 7701 7702 case JS_MAP_VALUE_ITERATOR_TYPE: 7703 value = effect = graph()->NewNode( 7704 simplified()->LoadElement( 7705 AccessBuilder::ForFixedArrayElement()), 7706 table, 7707 graph()->NewNode( 7708 simplified()->NumberAdd(), entry_start_position, 7709 jsgraph()->Constant(OrderedHashMap::kValueOffset)), 7710 effect, control); 7711 break; 7712 7713 case JS_MAP_KEY_VALUE_ITERATOR_TYPE: 7714 value = effect = graph()->NewNode( 7715 simplified()->LoadElement( 7716 AccessBuilder::ForFixedArrayElement()), 7717 table, 7718 graph()->NewNode( 7719 simplified()->NumberAdd(), entry_start_position, 7720 jsgraph()->Constant(OrderedHashMap::kValueOffset)), 7721 effect, control); 7722 value = effect = 7723 graph()->NewNode(javascript()->CreateKeyValueArray(), 7724 entry_key, value, context, effect); 7725 break; 7726 7727 default: 7728 UNREACHABLE(); 7729 } 7730 7731 // Store final {value} and {done} into the {iterator_result}. 7732 effect = 7733 graph()->NewNode(simplified()->StoreField( 7734 AccessBuilder::ForJSIteratorResultValue()), 7735 iterator_result, value, effect, control); 7736 effect = 7737 graph()->NewNode(simplified()->StoreField( 7738 AccessBuilder::ForJSIteratorResultDone()), 7739 iterator_result, done, effect, control); 7740 7741 controls[1] = control; 7742 effects[1] = effect; 7743 } 7744 7745 // Continue with next loop index. 7746 loop->ReplaceInput(1, graph()->NewNode(common()->IfTrue(), branch1)); 7747 eloop->ReplaceInput(1, etrue0); 7748 iloop->ReplaceInput(1, index); 7749 } 7750 } 7751 7752 control = effects[2] = graph()->NewNode(common()->Merge(2), 2, controls); 7753 effect = graph()->NewNode(common()->EffectPhi(2), 3, effects); 7754 } 7755 7756 // Yield the final {iterator_result}. 7757 ReplaceWithValue(node, iterator_result, effect, control); 7758 return Replace(iterator_result); 7759} 7760 7761Reduction JSCallReducer::ReduceArrayBufferIsView(Node* node) { 7762 JSCallNode n(node); 7763 Node* value = n.ArgumentOrUndefined(0, jsgraph()); 7764 RelaxEffectsAndControls(node); 7765 node->ReplaceInput(0, value); 7766 node->TrimInputCount(1); 7767 NodeProperties::ChangeOp(node, simplified()->ObjectIsArrayBufferView()); 7768 return Changed(node); 7769} 7770 7771Reduction JSCallReducer::ReduceArrayBufferViewAccessor( 7772 Node* node, InstanceType instance_type, FieldAccess const& access) { 7773 Node* receiver = NodeProperties::GetValueInput(node, 1); 7774 Effect effect{NodeProperties::GetEffectInput(node)}; 7775 Control control{NodeProperties::GetControlInput(node)}; 7776 7777 MapInference inference(broker(), receiver, effect); 7778 if (!inference.HaveMaps() || 7779 !inference.AllOfInstanceTypesAre(instance_type)) { 7780 return NoChange(); 7781 } 7782 7783 // Load the {receiver}s field. 7784 Node* value = effect = graph()->NewNode(simplified()->LoadField(access), 7785 receiver, effect, control); 7786 7787 // See if we can skip the detaching check. 7788 if (!dependencies()->DependOnArrayBufferDetachingProtector()) { 7789 // Check whether {receiver}s JSArrayBuffer was detached. 7790 Node* buffer = effect = graph()->NewNode( 7791 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), 7792 receiver, effect, control); 7793 Node* buffer_bit_field = effect = graph()->NewNode( 7794 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), 7795 buffer, effect, control); 7796 Node* check = graph()->NewNode( 7797 simplified()->NumberEqual(), 7798 graph()->NewNode( 7799 simplified()->NumberBitwiseAnd(), buffer_bit_field, 7800 jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)), 7801 jsgraph()->ZeroConstant()); 7802 7803 // TODO(turbofan): Ideally we would bail out here if the {receiver}s 7804 // JSArrayBuffer was detached, but there's no way to guard against 7805 // deoptimization loops right now, since the JSCall {node} is usually 7806 // created from a LOAD_IC inlining, and so there's no CALL_IC slot 7807 // from which we could use the speculation bit. 7808 value = graph()->NewNode( 7809 common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue), 7810 check, value, jsgraph()->ZeroConstant()); 7811 } 7812 7813 ReplaceWithValue(node, value, effect, control); 7814 return Replace(value); 7815} 7816 7817namespace { 7818uint32_t ExternalArrayElementSize(const ExternalArrayType element_type) { 7819 switch (element_type) { 7820#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ 7821 case kExternal##Type##Array: \ 7822 DCHECK_LE(sizeof(ctype), 8); \ 7823 return sizeof(ctype); 7824 TYPED_ARRAYS(TYPED_ARRAY_CASE) 7825 default: 7826 UNREACHABLE(); 7827#undef TYPED_ARRAY_CASE 7828 } 7829} 7830} // namespace 7831 7832Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access, 7833 ExternalArrayType element_type) { 7834 JSCallNode n(node); 7835 CallParameters const& p = n.Parameters(); 7836 size_t const element_size = ExternalArrayElementSize(element_type); 7837 Effect effect = n.effect(); 7838 Control control = n.control(); 7839 Node* receiver = n.receiver(); 7840 Node* offset = n.ArgumentOr(0, jsgraph()->ZeroConstant()); 7841 Node* value = nullptr; 7842 if (access == DataViewAccess::kSet) { 7843 value = n.ArgumentOrUndefined(1, jsgraph()); 7844 } 7845 const int endian_index = (access == DataViewAccess::kGet ? 1 : 2); 7846 Node* is_little_endian = 7847 n.ArgumentOr(endian_index, jsgraph()->FalseConstant()); 7848 7849 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 7850 return NoChange(); 7851 } 7852 7853 // Only do stuff if the {receiver} is really a DataView. 7854 MapInference inference(broker(), receiver, effect); 7855 if (!inference.HaveMaps() || 7856 !inference.AllOfInstanceTypesAre(JS_DATA_VIEW_TYPE)) { 7857 return NoChange(); 7858 } 7859 7860 // Check that the {offset} is within range for the {receiver}. 7861 HeapObjectMatcher m(receiver); 7862 if (m.HasResolvedValue() && m.Ref(broker()).IsJSDataView()) { 7863 // We only deal with DataViews here whose [[ByteLength]] is at least 7864 // {element_size}, as for all other DataViews it'll be out-of-bounds. 7865 JSDataViewRef dataview = m.Ref(broker()).AsJSDataView(); 7866 size_t length = dataview.byte_length(); 7867 if (length < element_size) return NoChange(); 7868 7869 // Check that the {offset} is within range of the {length}. 7870 Node* byte_length = jsgraph()->Constant(length - (element_size - 1)); 7871 offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), 7872 offset, byte_length, effect, control); 7873 } else { 7874 // We only deal with DataViews here that have Smi [[ByteLength]]s. 7875 Node* byte_length = effect = 7876 graph()->NewNode(simplified()->LoadField( 7877 AccessBuilder::ForJSArrayBufferViewByteLength()), 7878 receiver, effect, control); 7879 7880 if (element_size > 1) { 7881 // For non-byte accesses we also need to check that the {offset} 7882 // plus the {element_size}-1 fits within the given {byte_length}. 7883 // So to keep this as a single check on the {offset}, we subtract 7884 // the {element_size}-1 from the {byte_length} here (clamped to 7885 // positive safe integer range), and perform a check against that 7886 // with the {offset} below. 7887 byte_length = graph()->NewNode( 7888 simplified()->NumberMax(), jsgraph()->ZeroConstant(), 7889 graph()->NewNode(simplified()->NumberSubtract(), byte_length, 7890 jsgraph()->Constant(element_size - 1))); 7891 } 7892 7893 // Check that the {offset} is within range of the {byte_length}. 7894 offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), 7895 offset, byte_length, effect, control); 7896 } 7897 7898 // Coerce {is_little_endian} to boolean. 7899 is_little_endian = 7900 graph()->NewNode(simplified()->ToBoolean(), is_little_endian); 7901 7902 // Coerce {value} to Number. 7903 if (access == DataViewAccess::kSet) { 7904 value = effect = graph()->NewNode( 7905 simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball, 7906 p.feedback()), 7907 value, effect, control); 7908 } 7909 7910 // We need to retain either the {receiver} itself or it's backing 7911 // JSArrayBuffer to make sure that the GC doesn't collect the raw 7912 // memory. We default to {receiver} here, and only use the buffer 7913 // if we anyways have to load it (to reduce register pressure). 7914 Node* buffer_or_receiver = receiver; 7915 7916 if (!dependencies()->DependOnArrayBufferDetachingProtector()) { 7917 // Get the underlying buffer and check that it has not been detached. 7918 Node* buffer = effect = graph()->NewNode( 7919 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), 7920 receiver, effect, control); 7921 7922 // Bail out if the {buffer} was detached. 7923 Node* buffer_bit_field = effect = graph()->NewNode( 7924 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), 7925 buffer, effect, control); 7926 Node* check = graph()->NewNode( 7927 simplified()->NumberEqual(), 7928 graph()->NewNode( 7929 simplified()->NumberBitwiseAnd(), buffer_bit_field, 7930 jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)), 7931 jsgraph()->ZeroConstant()); 7932 effect = graph()->NewNode( 7933 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached, 7934 p.feedback()), 7935 check, effect, control); 7936 7937 // We can reduce register pressure by holding on to the {buffer} 7938 // now to retain the backing store memory. 7939 buffer_or_receiver = buffer; 7940 } 7941 7942 // Load the {receiver}s data pointer. 7943 Node* data_pointer = effect = graph()->NewNode( 7944 simplified()->LoadField(AccessBuilder::ForJSDataViewDataPointer()), 7945 receiver, effect, control); 7946 7947 switch (access) { 7948 case DataViewAccess::kGet: 7949 // Perform the load. 7950 value = effect = graph()->NewNode( 7951 simplified()->LoadDataViewElement(element_type), buffer_or_receiver, 7952 data_pointer, offset, is_little_endian, effect, control); 7953 break; 7954 case DataViewAccess::kSet: 7955 // Perform the store. 7956 effect = graph()->NewNode( 7957 simplified()->StoreDataViewElement(element_type), buffer_or_receiver, 7958 data_pointer, offset, value, is_little_endian, effect, control); 7959 value = jsgraph()->UndefinedConstant(); 7960 break; 7961 } 7962 7963 ReplaceWithValue(node, value, effect, control); 7964 return Changed(value); 7965} 7966 7967// ES6 section 18.2.2 isFinite ( number ) 7968Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node) { 7969 JSCallNode n(node); 7970 CallParameters const& p = n.Parameters(); 7971 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 7972 return NoChange(); 7973 } 7974 if (n.ArgumentCount() < 1) { 7975 Node* value = jsgraph()->FalseConstant(); 7976 ReplaceWithValue(node, value); 7977 return Replace(value); 7978 } 7979 7980 Effect effect = n.effect(); 7981 Control control = n.control(); 7982 Node* input = n.Argument(0); 7983 7984 input = effect = 7985 graph()->NewNode(simplified()->SpeculativeToNumber( 7986 NumberOperationHint::kNumberOrOddball, p.feedback()), 7987 input, effect, control); 7988 Node* value = graph()->NewNode(simplified()->NumberIsFinite(), input); 7989 ReplaceWithValue(node, value, effect); 7990 return Replace(value); 7991} 7992 7993// ES6 section 18.2.3 isNaN ( number ) 7994Reduction JSCallReducer::ReduceGlobalIsNaN(Node* node) { 7995 JSCallNode n(node); 7996 CallParameters const& p = n.Parameters(); 7997 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 7998 return NoChange(); 7999 } 8000 if (n.ArgumentCount() < 1) { 8001 Node* value = jsgraph()->TrueConstant(); 8002 ReplaceWithValue(node, value); 8003 return Replace(value); 8004 } 8005 8006 Effect effect = n.effect(); 8007 Control control = n.control(); 8008 Node* input = n.Argument(0); 8009 8010 input = effect = 8011 graph()->NewNode(simplified()->SpeculativeToNumber( 8012 NumberOperationHint::kNumberOrOddball, p.feedback()), 8013 input, effect, control); 8014 Node* value = graph()->NewNode(simplified()->NumberIsNaN(), input); 8015 ReplaceWithValue(node, value, effect); 8016 return Replace(value); 8017} 8018 8019// ES6 section 20.3.4.10 Date.prototype.getTime ( ) 8020Reduction JSCallReducer::ReduceDatePrototypeGetTime(Node* node) { 8021 Node* receiver = NodeProperties::GetValueInput(node, 1); 8022 Effect effect{NodeProperties::GetEffectInput(node)}; 8023 Control control{NodeProperties::GetControlInput(node)}; 8024 8025 MapInference inference(broker(), receiver, effect); 8026 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAre(JS_DATE_TYPE)) { 8027 return NoChange(); 8028 } 8029 8030 Node* value = effect = 8031 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForJSDateValue()), 8032 receiver, effect, control); 8033 ReplaceWithValue(node, value, effect, control); 8034 return Replace(value); 8035} 8036 8037// ES6 section 20.3.3.1 Date.now ( ) 8038Reduction JSCallReducer::ReduceDateNow(Node* node) { 8039 Node* effect = NodeProperties::GetEffectInput(node); 8040 Node* control = NodeProperties::GetControlInput(node); 8041 Node* value = effect = 8042 graph()->NewNode(simplified()->DateNow(), effect, control); 8043 ReplaceWithValue(node, value, effect, control); 8044 return Replace(value); 8045} 8046 8047// ES6 section 20.1.2.13 Number.parseInt ( string, radix ) 8048Reduction JSCallReducer::ReduceNumberParseInt(Node* node) { 8049 JSCallNode n(node); 8050 if (n.ArgumentCount() < 1) { 8051 Node* value = jsgraph()->NaNConstant(); 8052 ReplaceWithValue(node, value); 8053 return Replace(value); 8054 } 8055 8056 Effect effect = n.effect(); 8057 Control control = n.control(); 8058 Node* context = n.context(); 8059 FrameState frame_state = n.frame_state(); 8060 Node* object = n.Argument(0); 8061 Node* radix = n.ArgumentOrUndefined(1, jsgraph()); 8062 node->ReplaceInput(0, object); 8063 node->ReplaceInput(1, radix); 8064 node->ReplaceInput(2, context); 8065 node->ReplaceInput(3, frame_state); 8066 node->ReplaceInput(4, effect); 8067 node->ReplaceInput(5, control); 8068 node->TrimInputCount(6); 8069 NodeProperties::ChangeOp(node, javascript()->ParseInt()); 8070 return Changed(node); 8071} 8072 8073Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { 8074 JSCallNode n(node); 8075 CallParameters const& p = n.Parameters(); 8076 if (FLAG_force_slow_path) return NoChange(); 8077 if (n.ArgumentCount() < 1) return NoChange(); 8078 8079 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 8080 return NoChange(); 8081 } 8082 8083 Effect effect = n.effect(); 8084 Control control = n.control(); 8085 Node* regexp = n.receiver(); 8086 8087 // Only the initial JSRegExp map is valid here, since the following lastIndex 8088 // check as well as the lowered builtin call rely on a known location of the 8089 // lastIndex field. 8090 MapRef regexp_initial_map = 8091 native_context().regexp_function().initial_map(dependencies()); 8092 8093 MapInference inference(broker(), regexp, effect); 8094 if (!inference.Is(regexp_initial_map)) return inference.NoChange(); 8095 ZoneVector<MapRef> const& regexp_maps = inference.GetMaps(); 8096 8097 ZoneVector<PropertyAccessInfo> access_infos(graph()->zone()); 8098 AccessInfoFactory access_info_factory(broker(), dependencies(), 8099 graph()->zone()); 8100 8101 for (const MapRef& map : regexp_maps) { 8102 access_infos.push_back(broker()->GetPropertyAccessInfo( 8103 map, MakeRef(broker(), isolate()->factory()->exec_string()), 8104 AccessMode::kLoad, dependencies())); 8105 } 8106 8107 PropertyAccessInfo ai_exec = 8108 access_info_factory.FinalizePropertyAccessInfosAsOne(access_infos, 8109 AccessMode::kLoad); 8110 if (ai_exec.IsInvalid()) return inference.NoChange(); 8111 if (!ai_exec.IsFastDataConstant()) return inference.NoChange(); 8112 8113 // Do not reduce if the exec method is not on the prototype chain. 8114 base::Optional<JSObjectRef> holder = ai_exec.holder(); 8115 if (!holder.has_value()) return inference.NoChange(); 8116 8117 // Bail out if the exec method is not the original one. 8118 base::Optional<ObjectRef> constant = holder->GetOwnFastDataProperty( 8119 ai_exec.field_representation(), ai_exec.field_index(), dependencies()); 8120 if (!constant.has_value() || 8121 !constant->equals(native_context().regexp_exec_function())) { 8122 return inference.NoChange(); 8123 } 8124 8125 // Add proper dependencies on the {regexp}s [[Prototype]]s. 8126 dependencies()->DependOnStablePrototypeChains( 8127 ai_exec.lookup_start_object_maps(), kStartAtPrototype, holder.value()); 8128 8129 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, 8130 control, p.feedback()); 8131 8132 Node* context = n.context(); 8133 FrameState frame_state = n.frame_state(); 8134 Node* search = n.Argument(0); 8135 Node* search_string = effect = graph()->NewNode( 8136 simplified()->CheckString(p.feedback()), search, effect, control); 8137 8138 Node* lastIndex = effect = graph()->NewNode( 8139 simplified()->LoadField(AccessBuilder::ForJSRegExpLastIndex()), regexp, 8140 effect, control); 8141 8142 Node* lastIndexSmi = effect = graph()->NewNode( 8143 simplified()->CheckSmi(p.feedback()), lastIndex, effect, control); 8144 8145 Node* is_positive = graph()->NewNode(simplified()->NumberLessThanOrEqual(), 8146 jsgraph()->ZeroConstant(), lastIndexSmi); 8147 8148 effect = graph()->NewNode( 8149 simplified()->CheckIf(DeoptimizeReason::kNotASmi, p.feedback()), 8150 is_positive, effect, control); 8151 8152 node->ReplaceInput(0, regexp); 8153 node->ReplaceInput(1, search_string); 8154 node->ReplaceInput(2, context); 8155 node->ReplaceInput(3, frame_state); 8156 node->ReplaceInput(4, effect); 8157 node->ReplaceInput(5, control); 8158 node->TrimInputCount(6); 8159 NodeProperties::ChangeOp(node, javascript()->RegExpTest()); 8160 return Changed(node); 8161} 8162 8163// ES section #sec-number-constructor 8164Reduction JSCallReducer::ReduceNumberConstructor(Node* node) { 8165 JSCallNode n(node); 8166 Node* target = n.target(); 8167 Node* receiver = n.receiver(); 8168 Node* value = n.ArgumentOr(0, jsgraph()->ZeroConstant()); 8169 Node* context = n.context(); 8170 FrameState frame_state = n.frame_state(); 8171 8172 // Create the artificial frame state in the middle of the Number constructor. 8173 SharedFunctionInfoRef shared_info = 8174 native_context().number_function().shared(); 8175 Node* stack_parameters[] = {receiver}; 8176 int stack_parameter_count = arraysize(stack_parameters); 8177 Node* continuation_frame_state = 8178 CreateJavaScriptBuiltinContinuationFrameState( 8179 jsgraph(), shared_info, Builtin::kGenericLazyDeoptContinuation, 8180 target, context, stack_parameters, stack_parameter_count, frame_state, 8181 ContinuationFrameStateMode::LAZY); 8182 8183 // Convert the {value} to a Number. 8184 NodeProperties::ReplaceValueInputs(node, value); 8185 NodeProperties::ChangeOp(node, javascript()->ToNumberConvertBigInt()); 8186 NodeProperties::ReplaceFrameStateInput(node, continuation_frame_state); 8187 return Changed(node); 8188} 8189 8190Reduction JSCallReducer::ReduceBigIntAsN(Node* node, Builtin builtin) { 8191 DCHECK(builtin == Builtin::kBigIntAsIntN || 8192 builtin == Builtin::kBigIntAsUintN); 8193 8194 if (!jsgraph()->machine()->Is64()) return NoChange(); 8195 8196 JSCallNode n(node); 8197 CallParameters const& p = n.Parameters(); 8198 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { 8199 return NoChange(); 8200 } 8201 if (n.ArgumentCount() < 2) { 8202 return NoChange(); 8203 } 8204 8205 Effect effect = n.effect(); 8206 Control control = n.control(); 8207 Node* bits = n.Argument(0); 8208 Node* value = n.Argument(1); 8209 8210 NumberMatcher matcher(bits); 8211 if (matcher.IsInteger() && matcher.IsInRange(0, 64)) { 8212 const int bits_value = static_cast<int>(matcher.ResolvedValue()); 8213 value = effect = graph()->NewNode( 8214 (builtin == Builtin::kBigIntAsIntN 8215 ? simplified()->SpeculativeBigIntAsIntN(bits_value, p.feedback()) 8216 : simplified()->SpeculativeBigIntAsUintN(bits_value, 8217 p.feedback())), 8218 value, effect, control); 8219 ReplaceWithValue(node, value, effect); 8220 return Replace(value); 8221 } 8222 8223 return NoChange(); 8224} 8225 8226CompilationDependencies* JSCallReducer::dependencies() const { 8227 return broker()->dependencies(); 8228} 8229 8230Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } 8231 8232Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } 8233 8234Factory* JSCallReducer::factory() const { return isolate()->factory(); } 8235 8236NativeContextRef JSCallReducer::native_context() const { 8237 return broker()->target_native_context(); 8238} 8239 8240CommonOperatorBuilder* JSCallReducer::common() const { 8241 return jsgraph()->common(); 8242} 8243 8244JSOperatorBuilder* JSCallReducer::javascript() const { 8245 return jsgraph()->javascript(); 8246} 8247 8248SimplifiedOperatorBuilder* JSCallReducer::simplified() const { 8249 return jsgraph()->simplified(); 8250} 8251 8252} // namespace compiler 8253} // namespace internal 8254} // namespace v8 8255