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