Bug Summary

File:out/../deps/v8/src/compiler/js-call-reducer.cc
Warning:line 4075, column 5
Method called on moved-from object 'waitlist_' of type 'std::set'

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()) {
4
Control jumps to 'case kJSConstructWithSpread:' at line 2478
2474 case IrOpcode::kJSConstruct:
2475 return ReduceJSConstruct(node);
2476 case IrOpcode::kJSConstructWithArrayLike:
2477 return ReduceJSConstructWithArrayLike(node);
2478 case IrOpcode::kJSConstructWithSpread:
2479 return ReduceJSConstructWithSpread(node);
5
Calling 'JSCallReducer::ReduceJSConstructWithSpread'
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_);
1
Object 'waitlist_' of type 'std::set' is left in a valid but unspecified state after move
2497 for (Node* node : waitlist) {
2498 if (!node->IsDead()) {
2
Taking true branch
2499 Reduction const reduction = Reduce(node);
3
Calling 'JSCallReducer::Reduce'
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 =
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;
10
Assuming the condition is false
11
Taking false branch
4018 Node* const user = edge.from();
4019 switch (user->opcode()) {
12
Control jumps to the 'default' case at line 4069
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);
13
Method called on moved-from object 'waitlist_' of type 'std::set'
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) {
7
Assuming the condition is true
8
Taking true branch
4198 return ReduceCallOrConstructWithArrayLikeOrSpreadOfCreateArguments(
9
Calling 'JSCallReducer::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(
6
Calling 'JSCallReducer::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