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