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