Bug Summary

File:out/../deps/v8/src/compiler/js-call-reducer.cc
Warning:line 3805, column 18
Although the value stored to 'holder' is used in the enclosing expression, the value is never actually read from 'holder'

Annotated Source Code

Press '?' to see keyboard shortcuts

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