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 ¶ms.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