v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
js-call-reducer.cc
Go to the documentation of this file.
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
6
7#include <functional>
8#include <optional>
9
15#include "src/codegen/tnode.h"
36#include "src/flags/flags.h"
43#include "src/utils/utils.h"
44
45#ifdef V8_INTL_SUPPORT
47#endif
48
49namespace v8 {
50namespace internal {
51namespace compiler {
52
53// Shorter lambda declarations with less visual clutter.
54#define _ [&]()
55
57 static constexpr bool kMarkLoopExits = true;
58
59 public:
61 Node* effect = nullptr, Node* control = nullptr)
63 reducer->broker(), reducer->JSGraphForGraphAssembler(),
64 reducer->ZoneForGraphAssembler(), BranchSemantics::kJS,
65 [reducer](Node* n) { reducer->RevisitForGraphAssembler(n); },
67 dependencies_(reducer->dependencies()),
68 node_(node) {
70 effect ? effect : NodeProperties::GetEffectInput(node),
72
73 // Finish initializing the outermost catch scope.
74 bool has_handler =
77 }
78
80 std::unordered_set<Node*>* generated_calls_with_array_like_or_spread);
81 TNode<Object> ReduceMathUnary(const Operator* op);
82 TNode<Object> ReduceMathBinary(const Operator* op);
86 StringRef search_element_string);
88 TNode<Boolean> ReduceStringPrototypeEndsWith(StringRef search_element_string);
90 TNode<String> ReduceStringPrototypeCharAt(StringRef s, uint32_t index);
93
94 TNode<Object> TargetInput() const { return JSCallNode{node_ptr()}.target(); }
95
96 template <typename T>
98 return TNode<T>::UncheckedCast(JSCallNode{node_ptr()}.receiver());
99 }
100
102
103 Node* node_ptr() const { return node_; }
104
105 // Simplified operators.
107 TNode<Object> value,
113 CheckBoundsFlags flags = {});
114
115 // Common operators.
119 TNode<Object> Call4(const Callable& callable, TNode<Context> context,
120 TNode<Object> arg0, TNode<Object> arg1,
121 TNode<Object> arg2, TNode<Object> arg3);
122
123 // Javascript operators.
125 TNode<Object> arg0, TNode<Object> arg1,
126 TNode<Object> arg2, FrameState frame_state);
128 TNode<Object> arg0, TNode<Object> arg1,
129 TNode<Object> arg2, TNode<Object> arg3,
130 FrameState frame_state);
131
132 // Emplace a copy of the call node into the graph at current effect/control.
134
135 // Used in special cases in which we are certain CreateArray does not throw.
137 FrameState frame_state);
138
140 NativeContextRef native_context);
141
143 return NumberAdd(value, OneConstant());
144 }
145
147
148 template <typename T, typename U>
150 return AddNode<T>(
151 graph()->NewNode(common()->EnterMachineGraph(use_info), input));
152 }
153
154 template <typename T, typename U>
156 MachineRepresentation output_representation,
157 Type output_type) {
158 return AddNode<T>(graph()->NewNode(
159 common()->ExitMachineGraph(output_representation, output_type), input));
160 }
161
163 bool has_stability_dependency) {
164 // TODO(jgruber): Implement MapInference::InsertMapChecks in graph
165 // assembler.
166 if (!has_stability_dependency) {
167 Effect e = effect();
168 inference->InsertMapChecks(jsgraph(), &e, Control{control()}, feedback());
170 }
171 }
172
176 return AddNode<Number>(
177 graph()->NewNode(simplified()->ChangeFloat64HoleToTagged(), value));
178 }
179 return ConvertTaggedHoleToUndefined(value);
180 }
181
183 public:
185 using CatchFunction = std::function<void(TNode<Object>)>;
186
188 : gasm_(gasm), try_body_(try_body) {}
189
190 void Catch(const CatchFunction& catch_body) {
191 TNode<Object> handler_exception;
192 Effect handler_effect{nullptr};
193 Control handler_control{nullptr};
194
195 auto continuation = gasm_->MakeLabel();
196
197 // Try.
198 {
199 CatchScope catch_scope = CatchScope::Inner(gasm_->temp_zone(), gasm_);
200 try_body_();
202
203 catch_scope.MergeExceptionalPaths(&handler_exception, &handler_effect,
204 &handler_control);
205 }
206
207 // Catch.
208 {
209 gasm_->InitializeEffectControl(handler_effect, handler_control);
210 catch_body(handler_exception);
212 }
213
215 }
216
217 private:
220 };
221
223 return {this, try_body};
224 }
225
226 using ConditionFunction1 = std::function<TNode<Boolean>(TNode<Number>)>;
227 using StepFunction1 = std::function<TNode<Number>(TNode<Number>)>;
229 using For0BodyFunction = std::function<void(TNode<Number>)>;
230
231 public:
233 const ConditionFunction1& cond, const StepFunction1& step)
234 : gasm_(gasm),
235 initial_value_(initial_value),
236 cond_(cond),
237 step_(step) {}
238
239 void Do(const For0BodyFunction& body) {
240 auto loop_exit = gasm_->MakeLabel();
241
242 {
244
245 auto loop_header = loop_scope.loop_header_label();
246 auto loop_body = gasm_->MakeLabel();
247
248 gasm_->Goto(loop_header, initial_value_);
249
250 gasm_->Bind(loop_header);
251 TNode<Number> i = loop_header->PhiAt<Number>(0);
252
253 gasm_->BranchWithHint(cond_(i), &loop_body, &loop_exit,
255
256 gasm_->Bind(&loop_body);
257 body(i);
258 gasm_->Goto(loop_header, step_(i));
259 }
260
261 gasm_->Bind(&loop_exit);
262 }
263
264 private:
267
272 };
273
275 TNode<Number> initial_value = ZeroConstant();
276 auto cond = [=, this](TNode<Number> i) {
277 return NumberLessThan(i, excluded_limit);
278 };
279 auto step = [=, this](TNode<Number> i) {
280 return NumberAdd(i, OneConstant());
281 };
282 return {this, initial_value, cond, step};
283 }
284
285 ForBuilder0 Forever(TNode<Number> initial_value, const StepFunction1& step) {
286 return {this, initial_value,
287 [=, this](TNode<Number>) { return TrueConstant(); }, step};
288 }
289
290 using For1BodyFunction = std::function<void(TNode<Number>, TNode<Object>*)>;
292 public:
294 const ConditionFunction1& cond, const StepFunction1& step,
295 TNode<Object> initial_arg0)
296 : gasm_(gasm),
297 initial_value_(initial_value),
298 cond_(cond),
299 step_(step),
300 initial_arg0_(initial_arg0) {}
301
303 body_ = body;
304 return *this;
305 }
306
308 DCHECK(body_);
310
311 auto loop_exit = gasm_->MakeDeferredLabel(kPhiRepresentation);
312
313 {
315 loop_scope(gasm_);
316
317 auto loop_header = loop_scope.loop_header_label();
318 auto loop_body = gasm_->MakeDeferredLabel(kPhiRepresentation);
319
320 gasm_->Goto(loop_header, initial_value_, initial_arg0_);
321
322 gasm_->Bind(loop_header);
323 TNode<Number> i = loop_header->PhiAt<Number>(0);
324 arg0 = loop_header->PhiAt<Object>(1);
325
326 gasm_->BranchWithHint(cond_(i), &loop_body, &loop_exit,
327 BranchHint::kTrue, arg0);
328
329 gasm_->Bind(&loop_body);
330 body_(i, &arg0);
331 gasm_->Goto(loop_header, step_(i), arg0);
332 }
333
334 gasm_->Bind(&loop_exit);
335 return TNode<Object>::UncheckedCast(loop_exit.PhiAt<Object>(0));
336 }
337
338 void ValueIsUnused() { USE(Value()); }
339
340 private:
343
350 };
351
353 const StepFunction1& step, TNode<Object> initial_arg0) {
354 return {this, initial_value, cond, step, initial_arg0};
355 }
356
358 TNode<Object> initial_arg0) {
359 TNode<Number> initial_value = ZeroConstant();
360 auto cond = [=, this](TNode<Number> i) {
361 return NumberLessThan(i, excluded_limit);
362 };
363 auto step = [=, this](TNode<Number> i) {
364 return NumberAdd(i, OneConstant());
365 };
366 return {this, initial_value, cond, step, initial_arg0};
367 }
368
370 FrameState frame_state) {
371 IfNot(ObjectIsCallable(maybe_callable))
372 .Then(_ {
373 JSCallRuntime1(Runtime::kThrowCalledNonCallable, maybe_callable,
374 ContextInput(), frame_state);
375 Unreachable(); // The runtime call throws unconditionally.
376 })
377 .ExpectTrue();
378 }
379
380 const FeedbackSource& feedback() const {
381 CallParameters const& p = CallParametersOf(node_ptr()->op());
382 return p.feedback();
383 }
384
385 int ArgumentCount() const { return JSCallNode{node_ptr()}.ArgumentCount(); }
386
387 TNode<Object> Argument(int index) const {
389 }
390
391 template <typename T>
392 TNode<T> ArgumentAs(int index) const {
393 return TNode<T>::UncheckedCast(Argument(index));
394 }
395
398 ArgumentCount() > index ? Argument(index) : NaNConstant());
399 }
400
403 ArgumentCount() > index ? Argument(index) : UndefinedConstant());
404 }
405
408 ArgumentCount() > index ? Argument(index) : ZeroConstant());
409 }
410
415
419
421
422 private:
424 Node* const node_;
425};
426
431
432// This subclass bundles functionality specific to reducing iterating array
433// builtins.
435 public:
437 : JSCallReducerAssembler(reducer, node) {
438 DCHECK(v8_flags.turbo_inline_array_builtins);
439 }
440
442 const bool has_stability_dependency,
444 SharedFunctionInfoRef shared);
446 const bool has_stability_dependency,
449 SharedFunctionInfoRef shared);
451 const bool has_stability_dependency,
456 const bool has_stability_dependency,
461 const bool has_stability_dependency,
465 ArrayFindVariant variant);
467 MapInference* inference, const bool has_stability_dependency,
471 bool needs_fallback_builtin_call);
475
476 private:
477 // Returns {index,value}. Assumes that the map has not changed, but possibly
478 // the length and backing store.
481 TNode<Number> index) {
482 // Make sure that the access is still in bounds, since the callback could
483 // have changed the array's size.
485 index = CheckBounds(index, length);
486
487 // Reload the elements pointer before calling the callback, since the
488 // previous callback might have resized the array causing the elements
489 // buffer to be re-allocated.
490 TNode<HeapObject> elements =
493 AccessBuilder::ForFixedArrayElement(kind), elements, index);
494 return std::make_pair(index, value);
495 }
496
497 template <typename... Vars>
500 GraphAssemblerLabel<sizeof...(Vars)>* continue_label,
501 TNode<Vars>... vars) {
502 if (!IsHoleyElementsKind(kind)) return o;
503
504 auto if_not_hole = MakeLabel(MachineRepresentationOf<Vars>::value...);
505 BranchWithHint(HoleCheck(kind, o), continue_label, &if_not_hole,
506 BranchHint::kFalse, vars...);
507
508 // The contract is that we don't leak "the hole" into "user JavaScript",
509 // so we must rename the {element} here to explicitly exclude "the hole"
510 // from the type of {element}.
511 Bind(&if_not_hole);
512 return TypeGuardNonInternal(o);
513 }
514
526
533
539};
540
542 public:
544 : JSCallReducerAssembler(reducer, node) {
545 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
546 }
547
549
550 int ConstructArity() const {
552 }
553
555 return JSConstructNode{node_ptr()}.target();
556 }
557
559 return JSConstructNode{node_ptr()}.new_target();
560 }
561
562 private:
564 return AddNode<JSPromise>(
565 graph()->NewNode(javascript()->CreatePromise(), context, effect()));
566 }
567
569 TNode<Context> outer_context,
570 int slot_count) {
571 return AddNode<Context>(graph()->NewNode(
573 native_context.scope_info(broker()),
575 outer_context, effect(), control()));
576 }
577
578 void StoreContextSlot(TNode<Context> context, size_t slot_index,
579 TNode<Object> value) {
580 StoreField(AccessBuilder::ForContextSlot(slot_index), context, value);
581 }
582
584 SharedFunctionInfoRef shared, TNode<Context> context) {
585 DCHECK(shared.HasBuiltinId());
586 Handle<FeedbackCell> feedback_cell =
587 isolate()->factory()->many_closures_cell();
588 Callable const callable =
589 Builtins::CallableFor(isolate(), shared.builtin_id());
590 CodeRef code = MakeRef(broker(), *callable.code());
591 return AddNode<JSFunction>(graph()->NewNode(
592 javascript()->CreateClosure(shared, code), HeapConstant(feedback_cell),
593 context, effect(), control()));
594 }
595
597 TNode<JSFunction> reject, FrameState frame_state) {
599 const ConstructParameters& p = n.Parameters();
600 FeedbackSource no_feedback_source{};
601 Node* no_feedback = UndefinedConstant();
602 MayThrow(_ {
603 return AddNode<Object>(graph()->NewNode(
605 no_feedback_source,
607 executor, UndefinedConstant(), resolve, reject, no_feedback,
608 n.context(), frame_state, effect(), control()));
609 });
610 }
611
613 FrameState frame_state) {
615 const ConstructParameters& p = n.Parameters();
616 FeedbackSource no_feedback_source{};
617 Node* no_feedback = UndefinedConstant();
618 MayThrow(_ {
619 return AddNode<Object>(graph()->NewNode(
621 no_feedback_source,
623 reject, UndefinedConstant(), exception, no_feedback, n.context(),
624 frame_state, effect(), control()));
625 });
626 }
627};
628
630 public:
632 JSCallReducer* reducer, Node* node,
633 const FunctionTemplateInfoRef function_template_info,
634 FastApiCallFunction c_function, Node* receiver,
635 const SharedFunctionInfoRef shared, Node* target, const int arity,
636 Node* effect)
637 : JSCallReducerAssembler(reducer, node),
638 c_function_(c_function),
639 function_template_info_(function_template_info),
641 shared_(shared),
642 target_(target),
643 arity_(arity) {
644 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
646 }
647
650
651 // C arguments include the receiver at index 0. Thus C index 1 corresponds
652 // to the JS argument 0, etc.
653 // All functions in c_candidate_functions_ have the same number of
654 // arguments, so extract c_argument_count from the first function.
655 const int c_argument_count =
656 static_cast<int>(c_function_.signature->ArgumentCount());
657 CHECK_GE(c_argument_count, kReceiver);
658
659 const int slow_arg_count =
660 // Arguments for CallApiCallbackOptimizedXXX builtin including
661 // context, see CallApiCallbackOptimizedDescriptor.
663 // JS arguments.
665
666 const int value_input_count =
667 FastApiCallNode::ArityForArgc(c_argument_count, slow_arg_count);
668
669 base::SmallVector<Node*, kInlineSize> inputs(value_input_count +
671 int cursor = 0;
672 inputs[cursor++] = n.receiver();
673
674 // TODO(turbofan): Consider refactoring CFunctionInfo to distinguish
675 // between receiver and arguments, simplifying this (and related) spots.
676 int js_args_count = c_argument_count - kReceiver;
677 for (int i = 0; i < js_args_count; ++i) {
678 if (i < n.ArgumentCount()) {
679 inputs[cursor++] = n.Argument(i);
680 } else {
681 inputs[cursor++] = UndefinedConstant();
682 }
683 }
684
685 // Here we add the arguments for the slow call, which will be
686 // reconstructed at a later phase. Those are effectively the same
687 // arguments as for the fast call, but we want to have them as
688 // separate inputs, so that SimplifiedLowering can provide the best
689 // possible UseInfos for each of them. The inputs to FastApiCall
690 // look like:
691 // [receiver, ... C arguments, callback data,
692 // slow call code, external constant for function, argc,
693 // FunctionTemplateInfo, holder, receiver, ... JS arguments,
694 // context, new frame state].
695 bool no_profiling =
697 Callable call_api_callback = Builtins::CallableFor(
698 isolate(), no_profiling ? Builtin::kCallApiCallbackOptimizedNoProfiling
699 : Builtin::kCallApiCallbackOptimized);
700 CallInterfaceDescriptor cid = call_api_callback.descriptor();
701 DCHECK_EQ(cid.GetParameterCount() + (cid.HasContextParameter() ? 1 : 0),
703
704 CallDescriptor* call_descriptor =
707 ApiFunction api_function(function_template_info_.callback(broker()));
708 ExternalReference function_reference = ExternalReference::Create(
710 function_template_info_.c_functions(broker()).data(),
711 function_template_info_.c_signatures(broker()).data(),
712 static_cast<unsigned>(
713 function_template_info_.c_functions(broker()).size()));
714
715 Node* continuation_frame_state = CreateInlinedApiFunctionFrameState(
718
719 // Callback data value for fast Api calls. Unlike slow Api calls, the fast
720 // variant passes callback data directly.
721 inputs[cursor++] =
722 Constant(function_template_info_.callback_data(broker()).value());
723
724 inputs[cursor++] = HeapConstant(call_api_callback.code());
725 inputs[cursor++] = ExternalConstant(function_reference);
726 inputs[cursor++] = NumberConstant(arity_);
727 inputs[cursor++] = HeapConstant(function_template_info_.object());
728 inputs[cursor++] = receiver_;
729 for (int i = 0; i < arity_; ++i) {
730 inputs[cursor++] = Argument(i);
731 }
732 inputs[cursor++] = ContextInput();
733 inputs[cursor++] = continuation_frame_state;
734
735 inputs[cursor++] = effect();
736 inputs[cursor++] = control();
737
738 DCHECK_EQ(cursor, value_input_count + kEffectAndControl);
739
740 return FastApiCall(call_descriptor, inputs.begin(), inputs.size());
741 }
742
743 private:
744 static constexpr int kEffectAndControl = 2;
745
746 // Api function address, argc, FunctionTemplateInfo, context.
747 // See CallApiCallbackOptimizedDescriptor.
748 static constexpr int kSlowBuiltinParams = 4;
749 static constexpr int kReceiver = 1;
750
751 // Enough for creating FastApiCall node with two JS arguments.
752 static constexpr int kInlineSize = 16;
753
755 size_t inputs_size) {
756 return AddNode<Object>(graph()->NewNode(
757 simplified()->FastApiCall(c_function_, feedback(), descriptor),
758 static_cast<int>(inputs_size), inputs));
759 }
760
765 Node* const target_;
766 const int arity_;
767};
768
775
780
785
790
792 TNode<Number> limit,
793 CheckBoundsFlags flags) {
794 return AddNode<Number>(
795 graph()->NewNode(simplified()->CheckBounds(feedback(), flags), value,
796 limit, effect(), control()));
797}
798
802
807
809 TNode<Object> value) {
810 DCHECK(TypeCache::Get()->kFixedDoubleArrayLengthType.Is(
813 TypeGuard(TypeCache::Get()->kFixedArrayLengthType, value));
814}
815
817 const Callable& callable, TNode<Context> context, TNode<Object> arg0,
818 TNode<Object> arg1, TNode<Object> arg2, TNode<Object> arg3) {
819 // TODO(jgruber): Make this more generic. Currently it's fitted to its single
820 // callsite.
822 graph()->zone(), callable.descriptor(),
825
826 return TNode<Object>::UncheckedCast(Call(desc, HeapConstant(callable.code()),
827 arg0, arg1, arg2, arg3, context));
828}
829
832 TNode<Object> arg1, TNode<Object> arg2, FrameState frame_state) {
834 CallParameters const& p = n.Parameters();
835 return MayThrow(_ {
836 return AddNode<Object>(graph()->NewNode(
841 function, this_arg, arg0, arg1, arg2, n.feedback_vector(),
842 ContextInput(), frame_state, effect(), control()));
843 });
844}
845
849 FrameState frame_state) {
851 CallParameters const& p = n.Parameters();
852 return MayThrow(_ {
853 return AddNode<Object>(graph()->NewNode(
858 function, this_arg, arg0, arg1, arg2, arg3, n.feedback_vector(),
859 ContextInput(), frame_state, effect(), control()));
860 });
861}
862
871
873 TNode<Object> ctor, TNode<Number> size, FrameState frame_state) {
874 return AddNode<JSArray>(
875 graph()->NewNode(javascript()->CreateArray(1, std::nullopt), ctor, ctor,
876 size, ContextInput(), frame_state, effect(), control()));
877}
878
881 // TODO(jgruber): Port AllocationBuilder to JSGraphAssembler.
882 MapRef map = native_context.GetInitialJSArrayMap(broker(), kind);
883
885 ab.Allocate(map.instance_size(), AllocationType::kYoung, Type::Array());
886 ab.Store(AccessBuilder::ForMap(), map);
887 Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
889 empty_fixed_array);
890 ab.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array);
891 ab.Store(AccessBuilder::ForJSArrayLength(kind), jsgraph()->ZeroConstant());
892 for (int i = 0; i < map.GetInObjectProperties(); ++i) {
894 jsgraph()->UndefinedConstant());
895 }
896 Node* result = ab.Finish();
899}
900
906 NumberConstant(Map::Bits2::ElementsKindBits::kMask)),
907 NumberConstant(Map::Bits2::ElementsKindBits::kShift));
908}
909
911 TNode<Object> input = Argument(0);
912 TNode<Number> input_as_number = SpeculativeToNumber(input);
913 return TNode<Object>::UncheckedCast(graph()->NewNode(op, input_as_number));
914}
915
917 TNode<Object> left = Argument(0);
918 TNode<Object> right = ArgumentOrNaN(1);
919 TNode<Number> left_number = SpeculativeToNumber(left);
920 TNode<Number> right_number = SpeculativeToNumber(right);
922 graph()->NewNode(op, left_number, right_number));
923}
924
929
930 TNode<String> receiver_string = CheckString(receiver);
931 TNode<Number> start_smi = CheckSmi(start);
932
933 TNode<Number> length = StringLength(receiver_string);
934
935 TNode<Number> end_smi = SelectIf<Number>(IsUndefined(end))
936 .Then(_ { return length; })
937 .Else(_ { return CheckSmi(end); })
938 .ExpectFalse()
939 .Value();
940
941 TNode<Number> zero = TNode<Number>::UncheckedCast(ZeroConstant());
942 TNode<Number> finalStart = NumberMin(NumberMax(start_smi, zero), length);
943 TNode<Number> finalEnd = NumberMin(NumberMax(end_smi, zero), length);
944 TNode<Number> from = NumberMin(finalStart, finalEnd);
945 TNode<Number> to = NumberMax(finalStart, finalEnd);
946
947 return StringSubstring(receiver_string, from, to);
948}
949
951 StringRef search_element_string) {
952 DCHECK(search_element_string.IsContentAccessible());
955
956 TNode<String> receiver_string = CheckString(receiver);
957 TNode<Smi> start_smi = CheckSmi(start);
958 TNode<Number> length = StringLength(receiver_string);
959
960 TNode<Number> zero = ZeroConstant();
961 TNode<Number> clamped_start = NumberMin(NumberMax(start_smi, zero), length);
962
963 int search_string_length = search_element_string.length();
964 DCHECK(search_string_length <= JSCallReducer::kMaxInlineMatchSequence);
965
967
968 auto search_string_too_long =
969 NumberLessThan(NumberSubtract(length, clamped_start),
970 NumberConstant(search_string_length));
971
972 GotoIf(search_string_too_long, &out, BranchHint::kFalse, FalseConstant());
973
974 static_assert(String::kMaxLength <= kSmiMaxValue);
975
976 for (int i = 0; i < search_string_length; i++) {
978 TNode<Number> receiver_string_position = TNode<Number>::UncheckedCast(
979 TypeGuard(Type::UnsignedSmall(), NumberAdd(k, clamped_start)));
980 Node* receiver_string_char =
981 StringCharCodeAt(receiver_string, receiver_string_position);
982 Node* search_string_char = jsgraph()->ConstantNoHole(
983 search_element_string.GetChar(broker(), i).value());
984 auto is_equal = graph()->NewNode(simplified()->NumberEqual(),
985 search_string_char, receiver_string_char);
986 GotoIfNot(is_equal, &out, FalseConstant());
987 }
988
989 Goto(&out, TrueConstant());
990
991 Bind(&out);
992 return out.PhiAt<Boolean>(0);
993}
994
997 TNode<Object> search_element = ArgumentOrUndefined(0);
999
1000 TNode<String> receiver_string = CheckString(receiver);
1001 TNode<String> search_string = CheckString(search_element);
1002 TNode<Smi> start_smi = CheckSmi(start);
1003 TNode<Number> length = StringLength(receiver_string);
1004
1005 TNode<Number> zero = ZeroConstant();
1006 TNode<Number> clamped_start = NumberMin(NumberMax(start_smi, zero), length);
1007
1008 TNode<Number> search_string_length = StringLength(search_string);
1009
1011
1012 auto search_string_too_long = NumberLessThan(
1013 NumberSubtract(length, clamped_start), search_string_length);
1014
1015 GotoIf(search_string_too_long, &out, BranchHint::kFalse, FalseConstant());
1016
1017 static_assert(String::kMaxLength <= kSmiMaxValue);
1018
1019 ForZeroUntil(search_string_length).Do([&](TNode<Number> k) {
1020 TNode<Number> receiver_string_position = TNode<Number>::UncheckedCast(
1021 TypeGuard(Type::UnsignedSmall(), NumberAdd(k, clamped_start)));
1022 Node* receiver_string_char =
1023 StringCharCodeAt(receiver_string, receiver_string_position);
1024 if (!v8_flags.turbo_loop_variable) {
1025 // Without loop variable analysis, Turbofan's typer is unable to derive a
1026 // sufficiently precise type here. This is not a soundness problem, but
1027 // triggers graph verification errors. So we only insert the TypeGuard if
1028 // necessary.
1029 k = TypeGuard(Type::Unsigned32(), k);
1030 }
1031 Node* search_string_char = StringCharCodeAt(search_string, k);
1032 auto is_equal = graph()->NewNode(simplified()->NumberEqual(),
1033 receiver_string_char, search_string_char);
1034 GotoIfNot(is_equal, &out, FalseConstant());
1035 });
1036
1037 Goto(&out, TrueConstant());
1038
1039 Bind(&out);
1040 return out.PhiAt<Boolean>(0);
1041}
1042
1044 StringRef search_element_string) {
1045 DCHECK(search_element_string.IsContentAccessible());
1047 TNode<Object> end_position = ArgumentOrUndefined(1);
1048 TNode<Number> zero = ZeroConstant();
1049
1050 TNode<String> receiver_string = CheckString(receiver);
1051 TNode<Number> length = StringLength(receiver_string);
1052 int search_string_length = search_element_string.length();
1054
1055 TNode<Number> clamped_end =
1056 SelectIf<Number>(IsUndefined(end_position))
1057 .Then(_ { return length; })
1058 .Else(_ {
1059 return NumberMin(NumberMax(CheckSmi(end_position), zero), length);
1060 })
1061 .ExpectTrue()
1062 .Value();
1063
1065 NumberSubtract(clamped_end, NumberConstant(search_string_length));
1066
1068
1069 TNode<Boolean> search_string_too_long = NumberLessThan(start, zero);
1070 GotoIf(search_string_too_long, &out, BranchHint::kFalse, FalseConstant());
1071
1072 for (int i = 0; i < search_string_length; i++) {
1074 TNode<Number> receiver_string_position = TNode<Number>::UncheckedCast(
1076 Node* receiver_string_char =
1077 StringCharCodeAt(receiver_string, receiver_string_position);
1078 Node* search_string_char = jsgraph()->ConstantNoHole(
1079 search_element_string.GetChar(broker(), i).value());
1080 auto is_equal = graph()->NewNode(simplified()->NumberEqual(),
1081 receiver_string_char, search_string_char);
1082 GotoIfNot(is_equal, &out, FalseConstant());
1083 }
1084
1085 Goto(&out, TrueConstant());
1086
1087 Bind(&out);
1088 return out.PhiAt<Boolean>(0);
1089}
1090
1093 TNode<Object> search_string = ArgumentOrUndefined(0);
1094 TNode<Object> end_position = ArgumentOrUndefined(1);
1095 TNode<Number> zero = ZeroConstant();
1096
1097 TNode<String> receiver_string = CheckString(receiver);
1098 TNode<Number> length = StringLength(receiver_string);
1099 TNode<String> search_element_string = CheckString(search_string);
1100 TNode<Number> search_string_length = StringLength(search_element_string);
1101
1102 TNode<Number> clamped_end =
1103 SelectIf<Number>(IsUndefined(end_position))
1104 .Then(_ { return length; })
1105 .Else(_ {
1106 return NumberMin(NumberMax(CheckSmi(end_position), zero), length);
1107 })
1108 .ExpectTrue()
1109 .Value();
1110
1111 TNode<Number> start = NumberSubtract(clamped_end, search_string_length);
1112
1114
1115 TNode<Boolean> search_string_too_long = NumberLessThan(start, zero);
1116 GotoIf(search_string_too_long, &out, BranchHint::kFalse, FalseConstant());
1117
1118 ForZeroUntil(search_string_length).Do([&](TNode<Number> k) {
1119 TNode<Number> receiver_string_position = TNode<Number>::UncheckedCast(
1121 Node* receiver_string_char =
1122 StringCharCodeAt(receiver_string, receiver_string_position);
1123 if (!v8_flags.turbo_loop_variable) {
1124 // Without loop variable analysis, Turbofan's typer is unable to derive a
1125 // sufficiently precise type here. This is not a soundness problem, but
1126 // triggers graph verification errors. So we only insert the TypeGuard if
1127 // necessary.
1128 k = TypeGuard(Type::Unsigned32(), k);
1129 }
1130 Node* search_string_char = StringCharCodeAt(search_element_string, k);
1131 auto is_equal = graph()->NewNode(simplified()->NumberEqual(),
1132 receiver_string_char, search_string_char);
1133 GotoIfNot(is_equal, &out, FalseConstant());
1134 });
1135
1136 Goto(&out, TrueConstant());
1137
1138 Bind(&out);
1139 return out.PhiAt<Boolean>(0);
1140}
1141
1143 StringRef s, uint32_t index) {
1144 DCHECK(s.IsContentAccessible());
1145 if (s.IsOneByteRepresentation()) {
1146 OptionalObjectRef elem = s.GetCharAsStringOrUndefined(broker(), index);
1147 TNode<String> elem_string =
1148 elem.has_value()
1150 jsgraph()->ConstantNoHole(elem.value(), broker()))
1151 : EmptyStringConstant();
1152 return elem_string;
1153 } else {
1154 const uint32_t length = static_cast<uint32_t>(s.length());
1155 if (index >= length) return EmptyStringConstant();
1157 broker()
1158 ->local_isolate_or_isolate()
1159 ->factory()
1160 ->NewRawTwoByteString(1, AllocationType::kOld)
1161 .ToHandleChecked());
1162 flat->SeqTwoByteStringSet(0, s.GetChar(broker(), index).value());
1163 TNode<String> two_byte_elem =
1165 return two_byte_elem;
1166 }
1167}
1168
1171 TNode<Object> index = ArgumentOrZero(0);
1172
1173 TNode<String> receiver_string = CheckString(receiver);
1174 TNode<Number> index_smi = CheckSmi(index);
1175 TNode<Number> length = StringLength(receiver_string);
1176
1177 TNode<Number> bounded_index = CheckBounds(index_smi, length);
1178
1179 Node* result = StringCharCodeAt(receiver_string, bounded_index);
1180 TNode<String> result_string =
1182 return result_string;
1183}
1184
1189
1190 TNode<String> receiver_string = CheckString(receiver);
1191 TNode<Number> start_smi = CheckSmi(start);
1192
1193 TNode<Number> length = StringLength(receiver_string);
1194
1195 TNode<Number> end_smi = SelectIf<Number>(IsUndefined(end))
1196 .Then(_ { return length; })
1197 .Else(_ { return CheckSmi(end); })
1198 .ExpectFalse()
1199 .Value();
1200
1201 TNode<Number> zero = TNode<Number>::UncheckedCast(ZeroConstant());
1202 TNode<Number> from_untyped =
1203 SelectIf<Number>(NumberLessThan(start_smi, zero))
1204 .Then(_ { return NumberMax(NumberAdd(length, start_smi), zero); })
1205 .Else(_ { return NumberMin(start_smi, length); })
1206 .ExpectFalse()
1207 .Value();
1208 // {from} is always in non-negative Smi range, but our typer cannot figure
1209 // that out yet.
1210 TNode<Smi> from = TypeGuardUnsignedSmall(from_untyped);
1211
1212 TNode<Number> to_untyped =
1213 SelectIf<Number>(NumberLessThan(end_smi, zero))
1214 .Then(_ { return NumberMax(NumberAdd(length, end_smi), zero); })
1215 .Else(_ { return NumberMin(end_smi, length); })
1216 .ExpectFalse()
1217 .Value();
1218 // {to} is always in non-negative Smi range, but our typer cannot figure that
1219 // out yet.
1220 TNode<Smi> to = TypeGuardUnsignedSmall(to_untyped);
1221
1222 return SelectIf<String>(NumberLessThan(from, to))
1223 .Then(_ { return StringSubstring(receiver_string, from, to); })
1224 .Else(_ { return EmptyStringConstant(); })
1225 .ExpectTrue()
1226 .Value();
1227}
1228
1230 Builtin builtin) {
1232 TNode<Object> arguments_list = n.Argument(0);
1233
1234 auto call_builtin = MakeLabel();
1236
1237 // Check if {arguments_list} is a JSArray.
1238 GotoIf(ObjectIsSmi(arguments_list), &call_builtin);
1239 TNode<Map> arguments_list_map =
1241 TNode<HeapObject>::UncheckedCast(arguments_list));
1242 TNode<Number> arguments_list_instance_type = LoadField<Number>(
1243 AccessBuilder::ForMapInstanceType(), arguments_list_map);
1244 auto check_instance_type =
1245 NumberEqual(arguments_list_instance_type, NumberConstant(JS_ARRAY_TYPE));
1246 GotoIfNot(check_instance_type, &call_builtin);
1247
1248 // Check if {arguments_list} has PACKED_DOUBLE_ELEMENTS.
1249 TNode<Number> arguments_list_elements_kind =
1250 LoadMapElementsKind(arguments_list_map);
1251
1252 auto check_element_kind = NumberEqual(arguments_list_elements_kind,
1254 GotoIfNot(check_element_kind, &call_builtin);
1255
1256 // If {arguments_list} is a JSArray with PACKED_DOUBLE_ELEMENTS, calculate the
1257 // result with inlined loop.
1258 TNode<JSArray> array_arguments_list =
1259 TNode<JSArray>::UncheckedCast(arguments_list);
1260 Goto(&done, builtin == Builtin::kMathMax
1261 ? DoubleArrayMax(array_arguments_list)
1262 : DoubleArrayMin(array_arguments_list));
1263
1264 // Otherwise, call BuiltinMathMin/Max as usual.
1265 Bind(&call_builtin);
1266 TNode<Object> call = CopyNode();
1267 CallParameters const& p = n.Parameters();
1268
1269 // Set SpeculationMode to kDisallowSpeculation to avoid infinite
1270 // recursion.
1272 call, javascript()->CallWithArrayLike(
1273 p.frequency(), p.feedback(),
1275 Goto(&done, call);
1276
1277 Bind(&done);
1278 return done.PhiAt<Object>(0);
1279}
1280
1282 ZoneVector<MapRef> maps, bool needs_fallback_builtin_call) {
1284 TNode<Object> index = ArgumentOrZero(0);
1285
1286 TNode<Number> index_num = CheckSmi(index);
1288
1289 TNode<Map> receiver_map =
1291
1293
1294 for (MapRef map : maps) {
1295 DCHECK(map.supports_fast_array_iteration(broker()));
1296 auto correct_map_label = MakeLabel(), wrong_map_label = MakeLabel();
1297 TNode<Boolean> is_map_equal = ReferenceEqual(receiver_map, Constant(map));
1298 Branch(is_map_equal, &correct_map_label, &wrong_map_label);
1299 Bind(&correct_map_label);
1300
1301 TNode<Number> length = LoadJSArrayLength(receiver, map.elements_kind());
1302
1303 // If index is less than 0, then subtract from length.
1304 TNode<Boolean> cond = NumberLessThan(index_num, ZeroConstant());
1305 TNode<Number> real_index_num =
1306 SelectIf<Number>(cond)
1307 .Then(_ { return NumberAdd(length, index_num); })
1308 .Else(_ { return index_num; })
1309 .ExpectTrue() // Most common usage should be .at(-1)
1310 .Value();
1311
1312 // Bound checking.
1313 GotoIf(NumberLessThan(real_index_num, ZeroConstant()), &out,
1314 UndefinedConstant());
1315 GotoIfNot(NumberLessThan(real_index_num, length), &out,
1316 UndefinedConstant());
1317 if (v8_flags.turbo_typer_hardening) {
1318 real_index_num = CheckBounds(real_index_num, length,
1320 }
1321
1322 // Retrieving element at index.
1324 AccessBuilder::ForFixedArrayElement(map.elements_kind()), elements,
1325 real_index_num);
1326 if (IsHoleyElementsKind(map.elements_kind())) {
1327 // This case is needed in particular for HOLEY_DOUBLE_ELEMENTS: raw
1328 // doubles are stored in the FixedDoubleArray, and need to be converted to
1329 // HeapNumber or to Smi so that this function can return an Object. The
1330 // automatic converstion performed by
1331 // RepresentationChanger::GetTaggedRepresentationFor does not handle
1332 // holes, so we convert manually a potential hole here.
1333 element = ConvertHoleToUndefined(element, map.elements_kind());
1334 }
1335 Goto(&out, element);
1336
1337 Bind(&wrong_map_label);
1338 }
1339
1340 if (needs_fallback_builtin_call) {
1342 CallParameters const& p = n.Parameters();
1343
1344 // We set SpeculationMode to kDisallowSpeculation to avoid infinite
1345 // recursion on the node we're creating (since, after all, it's calling
1346 // Array.Prototype.at).
1347 const Operator* op = javascript()->Call(
1351 Node* fallback_builtin = node_ptr()->InputAt(0);
1352
1353 TNode<Object> res = MayThrow(_ {
1354 return AddNode<Object>(graph()->NewNode(
1355 op, fallback_builtin, receiver, index, n.feedback_vector(),
1356 ContextInput(), n.frame_state(), effect(), control()));
1357 });
1358 Goto(&out, res);
1359 } else {
1360 Goto(&out, UndefinedConstant());
1361 }
1362
1363 Bind(&out);
1364 return out.PhiAt<Object>(0);
1365}
1366
1368 MapInference* inference) {
1369 int const num_push_arguments = ArgumentCount();
1370 ZoneRefSet<Map> const& receiver_maps = inference->GetMaps();
1371
1373 base::SmallVector<Node*, 4> argument_nodes;
1374
1375 for (int i = 0; i < num_push_arguments; ++i) {
1377 argument_nodes.push_back(Argument(i));
1378 }
1379
1381 TNode<Map> receiver_map = LoadMap(receiver);
1382
1383 auto double_label = MakeLabel(argument_reps);
1384 auto smi_label = MakeLabel(argument_reps);
1385 auto object_label = MakeLabel(argument_reps);
1386
1387 for (size_t i = 0; i < receiver_maps.size(); i++) {
1388 MapRef map = receiver_maps[i];
1389 ElementsKind kind = map.elements_kind();
1390
1391 if (i < receiver_maps.size() - 1) {
1392 TNode<Boolean> is_map_equal = ReferenceEqual(receiver_map, Constant(map));
1394 GotoIf(is_map_equal, &double_label, argument_nodes);
1395 } else if (IsSmiElementsKind(kind)) {
1396 GotoIf(is_map_equal, &smi_label, argument_nodes);
1397 } else {
1398 GotoIf(is_map_equal, &object_label, argument_nodes);
1399 }
1400 } else {
1402 Goto(&double_label, argument_nodes);
1403 } else if (IsSmiElementsKind(kind)) {
1404 Goto(&smi_label, argument_nodes);
1405 } else {
1406 Goto(&object_label, argument_nodes);
1407 }
1408 }
1409 }
1410
1411 auto return_label = MakeLabel(MachineRepresentation::kTagged);
1412
1413 auto build_array_push = [&](ElementsKind kind,
1414 base::SmallVector<Node*, 1>& push_arguments) {
1415 // Only support PACKED_ELEMENTS and PACKED_DOUBLE_ELEMENTS, as "markers" of
1416 // what the elements array is (a FixedArray or FixedDoubleArray).
1418
1419 // Load the "length" property of the {receiver}.
1421 TNode<Number> return_value = length;
1422
1423 // Check if we have any {values} to push.
1424 if (num_push_arguments > 0) {
1425 // Compute the resulting "length" of the {receiver}.
1426 TNode<Number> new_length = return_value =
1427 NumberAdd(length, NumberConstant(num_push_arguments));
1428
1429 // Load the elements backing store of the {receiver}.
1431 TNode<Smi> elements_length = LoadFixedArrayBaseLength(elements);
1432
1433 elements = MaybeGrowFastElements(
1434 kind, feedback(), receiver, elements,
1435 NumberAdd(length, NumberConstant(num_push_arguments - 1)),
1436 elements_length);
1437
1438 // Update the JSArray::length field. Since this is observable,
1439 // there must be no other check after this.
1441
1442 // Append the {values} to the {elements}.
1443 for (int i = 0; i < num_push_arguments; ++i) {
1445 elements, NumberAdd(length, NumberConstant(i)),
1446 TNode<Object>::UncheckedCast(push_arguments[i]), kind);
1447 }
1448 }
1449
1450 Goto(&return_label, return_value);
1451 };
1452
1453 if (double_label.IsUsed()) {
1454 Bind(&double_label);
1455 base::SmallVector<Node*, 1> push_arguments(num_push_arguments);
1456 for (int i = 0; i < num_push_arguments; ++i) {
1457 Node* value =
1458 CheckNumber(TNode<Object>::UncheckedCast(double_label.PhiAt(i)));
1459 // Make sure we do not store signaling NaNs into double arrays.
1460 value = AddNode<Number>(
1461 graph()->NewNode(simplified()->NumberSilenceNaN(), value));
1462 push_arguments[i] = value;
1463 }
1464 build_array_push(PACKED_DOUBLE_ELEMENTS, push_arguments);
1465 }
1466
1467 if (smi_label.IsUsed()) {
1468 Bind(&smi_label);
1469 base::SmallVector<Node*, 4> push_arguments(num_push_arguments);
1470 for (int i = 0; i < num_push_arguments; ++i) {
1471 Node* value = CheckSmi(TNode<Object>::UncheckedCast(smi_label.PhiAt(i)));
1472 push_arguments[i] = value;
1473 }
1474 Goto(&object_label, push_arguments);
1475 }
1476
1477 if (object_label.IsUsed()) {
1478 Bind(&object_label);
1479 base::SmallVector<Node*, 1> push_arguments(num_push_arguments);
1480 for (int i = 0; i < num_push_arguments; ++i) {
1481 push_arguments[i] = object_label.PhiAt(i);
1482 }
1483 build_array_push(PACKED_ELEMENTS, push_arguments);
1484 }
1485
1486 Bind(&return_label);
1487 return TNode<Number>::UncheckedCast(return_label.PhiAt(0));
1488}
1489
1490namespace {
1491
1492struct ForEachFrameStateParams {
1493 JSGraph* jsgraph;
1494 SharedFunctionInfoRef shared;
1495 TNode<Context> context;
1496 TNode<Object> target;
1498 TNode<Object> receiver;
1499 TNode<Object> callback;
1500 TNode<Object> this_arg;
1501 TNode<Object> original_length;
1502};
1503
1504FrameState ForEachLoopLazyFrameState(const ForEachFrameStateParams& params,
1505 TNode<Object> k) {
1506 Builtin builtin = Builtin::kArrayForEachLoopLazyDeoptContinuation;
1507 Node* checkpoint_params[] = {params.receiver, params.callback,
1508 params.this_arg, k, params.original_length};
1510 params.jsgraph, params.shared, builtin, params.target, params.context,
1511 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
1513}
1514
1515FrameState ForEachLoopEagerFrameState(const ForEachFrameStateParams& params,
1516 TNode<Object> k) {
1517 Builtin builtin = Builtin::kArrayForEachLoopEagerDeoptContinuation;
1518 Node* checkpoint_params[] = {params.receiver, params.callback,
1519 params.this_arg, k, params.original_length};
1521 params.jsgraph, params.shared, builtin, params.target, params.context,
1522 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
1524}
1525
1526} // namespace
1527
1528TNode<Object>
1530 MapInference* inference, const bool has_stability_dependency,
1533 TNode<Context> context = ContextInput();
1534 TNode<Object> target = TargetInput();
1536 TNode<Object> fncallback = ArgumentOrUndefined(0);
1538
1540
1541 ForEachFrameStateParams frame_state_params{
1543 receiver, fncallback, this_arg, original_length};
1544
1545 ThrowIfNotCallable(fncallback, ForEachLoopLazyFrameState(frame_state_params,
1546 ZeroConstant()));
1547
1549 Checkpoint(ForEachLoopEagerFrameState(frame_state_params, k));
1550
1551 // Deopt if the map has changed during the iteration.
1552 MaybeInsertMapChecks(inference, has_stability_dependency);
1553
1554 TNode<Object> element;
1555 std::tie(k, element) = SafeLoadElement(kind, receiver, k);
1556
1557 auto continue_label = MakeLabel();
1558 element = MaybeSkipHole(element, kind, &continue_label);
1559
1560 TNode<Number> next_k = NumberAdd(k, OneConstant());
1561 JSCall3(fncallback, this_arg, element, k, receiver,
1562 ForEachLoopLazyFrameState(frame_state_params, next_k));
1563
1564 Goto(&continue_label);
1565 Bind(&continue_label);
1566 });
1567
1568 return UndefinedConstant();
1569}
1570
1571namespace {
1572
1573struct ReduceFrameStateParams {
1575 SharedFunctionInfoRef shared;
1577 TNode<Context> context;
1578 TNode<Object> target;
1579 FrameState outer_frame_state;
1580};
1581
1582FrameState ReducePreLoopLazyFrameState(const ReduceFrameStateParams& params,
1583 TNode<Object> receiver,
1584 TNode<Object> callback, TNode<Object> k,
1585 TNode<Number> original_length) {
1586 Builtin builtin = (params.direction == ArrayReduceDirection::kLeft)
1587 ? Builtin::kArrayReduceLoopLazyDeoptContinuation
1588 : Builtin::kArrayReduceRightLoopLazyDeoptContinuation;
1589 Node* checkpoint_params[] = {receiver, callback, k, original_length};
1591 params.jsgraph, params.shared, builtin, params.target, params.context,
1592 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
1594}
1595
1596FrameState ReducePreLoopEagerFrameState(const ReduceFrameStateParams& params,
1597 TNode<Object> receiver,
1598 TNode<Object> callback,
1599 TNode<Number> original_length) {
1600 Builtin builtin =
1601 (params.direction == ArrayReduceDirection::kLeft)
1602 ? Builtin::kArrayReducePreLoopEagerDeoptContinuation
1603 : Builtin::kArrayReduceRightPreLoopEagerDeoptContinuation;
1604 Node* checkpoint_params[] = {receiver, callback, original_length};
1606 params.jsgraph, params.shared, builtin, params.target, params.context,
1607 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
1609}
1610
1611FrameState ReduceLoopLazyFrameState(const ReduceFrameStateParams& params,
1612 TNode<Object> receiver,
1613 TNode<Object> callback, TNode<Object> k,
1614 TNode<Number> original_length) {
1615 Builtin builtin = (params.direction == ArrayReduceDirection::kLeft)
1616 ? Builtin::kArrayReduceLoopLazyDeoptContinuation
1617 : Builtin::kArrayReduceRightLoopLazyDeoptContinuation;
1618 Node* checkpoint_params[] = {receiver, callback, k, original_length};
1620 params.jsgraph, params.shared, builtin, params.target, params.context,
1621 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
1623}
1624
1625FrameState ReduceLoopEagerFrameState(const ReduceFrameStateParams& params,
1626 TNode<Object> receiver,
1627 TNode<Object> callback, TNode<Object> k,
1628 TNode<Number> original_length,
1629 TNode<Object> accumulator) {
1630 Builtin builtin = (params.direction == ArrayReduceDirection::kLeft)
1631 ? Builtin::kArrayReduceLoopEagerDeoptContinuation
1632 : Builtin::kArrayReduceRightLoopEagerDeoptContinuation;
1633 Node* checkpoint_params[] = {receiver, callback, k, original_length,
1634 accumulator};
1636 params.jsgraph, params.shared, builtin, params.target, params.context,
1637 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
1639}
1640
1641} // namespace
1642
1644 MapInference* inference, const bool has_stability_dependency,
1646 SharedFunctionInfoRef shared) {
1648 TNode<Context> context = ContextInput();
1649 TNode<Object> target = TargetInput();
1651 TNode<Object> fncallback = ArgumentOrUndefined(0);
1652
1653 ReduceFrameStateParams frame_state_params{
1655
1657
1658 // Set up variable behavior depending on the reduction kind (left/right).
1659 TNode<Number> k;
1660 StepFunction1 step;
1661 ConditionFunction1 cond;
1662 TNode<Number> zero = ZeroConstant();
1663 TNode<Number> one = OneConstant();
1665 k = zero;
1666 step = [&](TNode<Number> i) { return NumberAdd(i, one); };
1667 cond = [&](TNode<Number> i) { return NumberLessThan(i, original_length); };
1668 } else {
1670 step = [&](TNode<Number> i) { return NumberSubtract(i, one); };
1671 cond = [&](TNode<Number> i) { return NumberLessThanOrEqual(zero, i); };
1672 }
1673
1675 fncallback, ReducePreLoopLazyFrameState(frame_state_params, receiver,
1676 fncallback, k, original_length));
1677
1678 // Set initial accumulator value.
1679 TNode<Object> accumulator;
1680 if (ArgumentCount() > 1) {
1681 accumulator = Argument(1); // Initial value specified by the user.
1682 } else {
1683 // The initial value was not specified by the user. In this case, the first
1684 // (or last in the case of reduceRight) non-holey value of the array is
1685 // used. Loop until we find it. If not found, trigger a deopt.
1686 // TODO(jgruber): The deopt does not seem necessary. Instead we could simply
1687 // throw the TypeError here from optimized code.
1688 auto found_initial_element = MakeLabel(MachineRepresentation::kTagged,
1690 Forever(k, step).Do([&](TNode<Number> k) {
1691 Checkpoint(ReducePreLoopEagerFrameState(frame_state_params, receiver,
1692 fncallback, original_length));
1693 CheckIf(cond(k), DeoptimizeReason::kNoInitialElement);
1694
1695 TNode<Object> element;
1696 std::tie(k, element) = SafeLoadElement(kind, receiver, k);
1697
1698 auto continue_label = MakeLabel();
1699 GotoIf(HoleCheck(kind, element), &continue_label);
1700 Goto(&found_initial_element, k, TypeGuardNonInternal(element));
1701
1702 Bind(&continue_label);
1703 });
1704 Unreachable(); // The loop is exited either by deopt or a jump to below.
1705
1706 // TODO(jgruber): This manual fiddling with blocks could be avoided by
1707 // implementing a `break` mechanic for loop builders.
1708 Bind(&found_initial_element);
1709 k = step(found_initial_element.PhiAt<Number>(0));
1710 accumulator = found_initial_element.PhiAt<Object>(1);
1711 }
1712
1714 For1(k, cond, step, accumulator)
1715 .Do([&](TNode<Number> k, TNode<Object>* accumulator) {
1716 Checkpoint(ReduceLoopEagerFrameState(frame_state_params, receiver,
1717 fncallback, k, original_length,
1718 *accumulator));
1719
1720 // Deopt if the map has changed during the iteration.
1721 MaybeInsertMapChecks(inference, has_stability_dependency);
1722
1723 TNode<Object> element;
1724 std::tie(k, element) = SafeLoadElement(kind, receiver, k);
1725
1726 auto continue_label = MakeLabel(MachineRepresentation::kTagged);
1727 element =
1728 MaybeSkipHole(element, kind, &continue_label, *accumulator);
1729
1730 TNode<Number> next_k = step(k);
1731 TNode<Object> next_accumulator = JSCall4(
1732 fncallback, UndefinedConstant(), *accumulator, element, k,
1733 receiver,
1734 ReduceLoopLazyFrameState(frame_state_params, receiver,
1735 fncallback, next_k, original_length));
1736 Goto(&continue_label, next_accumulator);
1737
1738 Bind(&continue_label);
1739 *accumulator = continue_label.PhiAt<Object>(0);
1740 })
1741 .Value();
1742
1743 return result;
1744}
1745
1746namespace {
1747
1748struct MapFrameStateParams {
1750 SharedFunctionInfoRef shared;
1751 TNode<Context> context;
1752 TNode<Object> target;
1757 std::optional<TNode<JSArray>> a;
1758 TNode<Object> original_length;
1759};
1760
1761FrameState MapPreLoopLazyFrameState(const MapFrameStateParams& params) {
1762 DCHECK(!params.a);
1763 Node* checkpoint_params[] = {params.receiver, params.callback,
1764 params.this_arg, params.original_length};
1766 params.jsgraph, params.shared,
1767 Builtin::kArrayMapPreLoopLazyDeoptContinuation, params.target,
1768 params.context, checkpoint_params, arraysize(checkpoint_params),
1769 params.outer_frame_state, ContinuationFrameStateMode::LAZY);
1770}
1771
1772FrameState MapLoopLazyFrameState(const MapFrameStateParams& params,
1773 TNode<Number> k) {
1774 Node* checkpoint_params[] = {
1775 params.receiver, params.callback, params.this_arg, *params.a, k,
1776 params.original_length};
1778 params.jsgraph, params.shared,
1779 Builtin::kArrayMapLoopLazyDeoptContinuation, params.target,
1780 params.context, checkpoint_params, arraysize(checkpoint_params),
1781 params.outer_frame_state, ContinuationFrameStateMode::LAZY);
1782}
1783
1784FrameState MapLoopEagerFrameState(const MapFrameStateParams& params,
1785 TNode<Number> k) {
1786 Node* checkpoint_params[] = {
1787 params.receiver, params.callback, params.this_arg, *params.a, k,
1788 params.original_length};
1790 params.jsgraph, params.shared,
1791 Builtin::kArrayMapLoopEagerDeoptContinuation, params.target,
1792 params.context, checkpoint_params, arraysize(checkpoint_params),
1793 params.outer_frame_state, ContinuationFrameStateMode::EAGER);
1794}
1795
1796} // namespace
1797
1799 MapInference* inference, const bool has_stability_dependency,
1803 TNode<Context> context = ContextInput();
1804 TNode<Object> target = TargetInput();
1806 TNode<Object> fncallback = ArgumentOrUndefined(0);
1808
1810
1811 // If the array length >= kMaxFastArrayLength, then CreateArray
1812 // will create a dictionary. We should deopt in this case, and make sure
1813 // not to attempt inlining again.
1816
1817 // Even though {JSCreateArray} is not marked as {kNoThrow}, we can elide the
1818 // exceptional projections because it cannot throw with the given
1819 // parameters.
1820 TNode<Object> array_ctor =
1821 Constant(native_context.GetInitialJSArrayMap(broker(), kind)
1822 .GetConstructor(broker()));
1823
1824 MapFrameStateParams frame_state_params{
1826 receiver, fncallback, this_arg, {} /* TBD */, original_length};
1827
1828 TNode<JSArray> a =
1830 MapPreLoopLazyFrameState(frame_state_params));
1831 frame_state_params.a = a;
1832
1833 ThrowIfNotCallable(fncallback,
1834 MapLoopLazyFrameState(frame_state_params, ZeroConstant()));
1835
1837 Checkpoint(MapLoopEagerFrameState(frame_state_params, k));
1838 MaybeInsertMapChecks(inference, has_stability_dependency);
1839
1840 TNode<Object> element;
1841 std::tie(k, element) = SafeLoadElement(kind, receiver, k);
1842
1843 auto continue_label = MakeLabel();
1844 element = MaybeSkipHole(element, kind, &continue_label);
1845
1846 TNode<Object> v = JSCall3(fncallback, this_arg, element, k, receiver,
1847 MapLoopLazyFrameState(frame_state_params, k));
1848
1849 // The array {a} should be HOLEY_SMI_ELEMENTS because we'd only come into
1850 // this loop if the input array length is non-zero, and "new Array({x > 0})"
1851 // always produces a HOLEY array.
1852 MapRef holey_double_map =
1853 native_context.GetInitialJSArrayMap(broker(), HOLEY_DOUBLE_ELEMENTS);
1854 MapRef holey_map =
1855 native_context.GetInitialJSArrayMap(broker(), HOLEY_ELEMENTS);
1856 TransitionAndStoreElement(holey_double_map, holey_map, a, k, v);
1857
1858 Goto(&continue_label);
1859 Bind(&continue_label);
1860 });
1861
1862 return a;
1863}
1864
1865namespace {
1866
1867struct FilterFrameStateParams {
1869 SharedFunctionInfoRef shared;
1870 TNode<Context> context;
1871 TNode<Object> target;
1878};
1879
1880FrameState FilterLoopLazyFrameState(const FilterFrameStateParams& params,
1882 TNode<Object> element) {
1883 Node* checkpoint_params[] = {params.receiver,
1884 params.callback,
1885 params.this_arg,
1886 params.a,
1887 k,
1888 params.original_length,
1889 element,
1890 to};
1892 params.jsgraph, params.shared,
1893 Builtin::kArrayFilterLoopLazyDeoptContinuation, params.target,
1894 params.context, checkpoint_params, arraysize(checkpoint_params),
1895 params.outer_frame_state, ContinuationFrameStateMode::LAZY);
1896}
1897
1898FrameState FilterLoopEagerPostCallbackFrameState(
1899 const FilterFrameStateParams& params, TNode<Number> k, TNode<Number> to,
1900 TNode<Object> element, TNode<Object> callback_value) {
1901 // Note that we are intentionally reusing the
1902 // Builtin::kArrayFilterLoopLazyDeoptContinuation as an *eager* entry
1903 // point in this case. This is safe, because re-evaluating a [ToBoolean]
1904 // coercion is safe.
1905 Node* checkpoint_params[] = {params.receiver,
1906 params.callback,
1907 params.this_arg,
1908 params.a,
1909 k,
1910 params.original_length,
1911 element,
1912 to,
1913 callback_value};
1915 params.jsgraph, params.shared,
1916 Builtin::kArrayFilterLoopLazyDeoptContinuation, params.target,
1917 params.context, checkpoint_params, arraysize(checkpoint_params),
1918 params.outer_frame_state, ContinuationFrameStateMode::EAGER);
1919}
1920
1921FrameState FilterLoopEagerFrameState(const FilterFrameStateParams& params,
1922 TNode<Number> k, TNode<Number> to) {
1923 Node* checkpoint_params[] = {params.receiver,
1924 params.callback,
1925 params.this_arg,
1926 params.a,
1927 k,
1928 params.original_length,
1929 to};
1931 params.jsgraph, params.shared,
1932 Builtin::kArrayFilterLoopEagerDeoptContinuation, params.target,
1933 params.context, checkpoint_params, arraysize(checkpoint_params),
1934 params.outer_frame_state, ContinuationFrameStateMode::EAGER);
1935}
1936
1937} // namespace
1938
1939TNode<JSArray>
1941 MapInference* inference, const bool has_stability_dependency,
1945 TNode<Context> context = ContextInput();
1946 TNode<Object> target = TargetInput();
1948 TNode<Object> fncallback = ArgumentOrUndefined(0);
1950
1951 // The output array is packed (filter doesn't visit holes).
1952 const ElementsKind packed_kind = GetPackedElementsKind(kind);
1954
1956
1957 FilterFrameStateParams frame_state_params{
1959 receiver, fncallback, this_arg, a, original_length};
1960
1961 // This frame state doesn't ever call the deopt continuation, it's only
1962 // necessary to specify a continuation in order to handle the exceptional
1963 // case. We don't have all the values available to completely fill out
1964 // the checkpoint parameters yet, but that's okay because it'll never be
1965 // called.
1966 TNode<Number> zero = ZeroConstant();
1967 ThrowIfNotCallable(fncallback, FilterLoopLazyFrameState(frame_state_params,
1968 zero, zero, zero));
1969
1970 TNode<Number> initial_a_length = zero;
1971 For1ZeroUntil(original_length, initial_a_length)
1972 .Do([&](TNode<Number> k, TNode<Object>* a_length_object) {
1973 TNode<Number> a_length = TNode<Number>::UncheckedCast(*a_length_object);
1974 Checkpoint(FilterLoopEagerFrameState(frame_state_params, k, a_length));
1975 MaybeInsertMapChecks(inference, has_stability_dependency);
1976
1977 TNode<Object> element;
1978 std::tie(k, element) = SafeLoadElement(kind, receiver, k);
1979
1980 auto continue_label = MakeLabel(MachineRepresentation::kTaggedSigned);
1981 element = MaybeSkipHole(element, kind, &continue_label, a_length);
1982
1984 fncallback, this_arg, element, k, receiver,
1985 FilterLoopLazyFrameState(frame_state_params, k, a_length, element));
1986
1987 // We need an eager frame state for right after the callback function
1988 // returned, just in case an attempt to grow the output array fails.
1989 Checkpoint(FilterLoopEagerPostCallbackFrameState(frame_state_params, k,
1990 a_length, element, v));
1991
1992 GotoIfNot(ToBoolean(v), &continue_label, a_length);
1993
1994 // Since the callback returned a trueish value, store the element in a.
1995 {
1996 TNode<Number> a_length1 = TypeGuardFixedArrayLength(a_length);
1997 TNode<FixedArrayBase> elements = LoadElements(a);
1998 elements = MaybeGrowFastElements(kind, FeedbackSource{}, a, elements,
1999 a_length1,
2000 LoadFixedArrayBaseLength(elements));
2001
2002 TNode<Number> new_a_length = NumberInc(a_length1);
2003 StoreJSArrayLength(a, new_a_length, kind);
2004 StoreFixedArrayBaseElement(elements, a_length1, element, kind);
2005
2006 Goto(&continue_label, new_a_length);
2007 }
2008
2009 Bind(&continue_label);
2010 *a_length_object =
2011 TNode<Object>::UncheckedCast(continue_label.PhiAt(0));
2012 })
2013 .ValueIsUnused();
2014
2015 return a;
2016}
2017
2018namespace {
2019
2020struct FindFrameStateParams {
2022 SharedFunctionInfoRef shared;
2023 TNode<Context> context;
2024 TNode<Object> target;
2030};
2031
2032FrameState FindLoopLazyFrameState(const FindFrameStateParams& params,
2033 TNode<Number> k, ArrayFindVariant variant) {
2034 Builtin builtin = (variant == ArrayFindVariant::kFind)
2035 ? Builtin::kArrayFindLoopLazyDeoptContinuation
2036 : Builtin::kArrayFindIndexLoopLazyDeoptContinuation;
2037 Node* checkpoint_params[] = {params.receiver, params.callback,
2038 params.this_arg, k, params.original_length};
2040 params.jsgraph, params.shared, builtin, params.target, params.context,
2041 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
2043}
2044
2045FrameState FindLoopEagerFrameState(const FindFrameStateParams& params,
2046 TNode<Number> k, ArrayFindVariant variant) {
2047 Builtin builtin = (variant == ArrayFindVariant::kFind)
2048 ? Builtin::kArrayFindLoopEagerDeoptContinuation
2049 : Builtin::kArrayFindIndexLoopEagerDeoptContinuation;
2050 Node* checkpoint_params[] = {params.receiver, params.callback,
2051 params.this_arg, k, params.original_length};
2053 params.jsgraph, params.shared, builtin, params.target, params.context,
2054 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
2056}
2057
2058FrameState FindLoopAfterCallbackLazyFrameState(
2059 const FindFrameStateParams& params, TNode<Number> next_k,
2060 TNode<Object> if_found_value, ArrayFindVariant variant) {
2061 Builtin builtin =
2062 (variant == ArrayFindVariant::kFind)
2063 ? Builtin::kArrayFindLoopAfterCallbackLazyDeoptContinuation
2064 : Builtin::kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation;
2065 Node* checkpoint_params[] = {params.receiver, params.callback,
2066 params.this_arg, next_k,
2067 params.original_length, if_found_value};
2069 params.jsgraph, params.shared, builtin, params.target, params.context,
2070 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
2072}
2073
2074} // namespace
2075
2077 MapInference* inference, const bool has_stability_dependency,
2081 TNode<Context> context = ContextInput();
2082 TNode<Object> target = TargetInput();
2084 TNode<Object> fncallback = ArgumentOrUndefined(0);
2086
2088
2089 FindFrameStateParams frame_state_params{
2091 receiver, fncallback, this_arg, original_length};
2092
2094 fncallback,
2095 FindLoopLazyFrameState(frame_state_params, ZeroConstant(), variant));
2096
2097 const bool is_find_variant = (variant == ArrayFindVariant::kFind);
2099
2101 Checkpoint(FindLoopEagerFrameState(frame_state_params, k, variant));
2102 MaybeInsertMapChecks(inference, has_stability_dependency);
2103
2104 TNode<Object> element;
2105 std::tie(k, element) = SafeLoadElement(kind, receiver, k);
2106
2108 element = ConvertHoleToUndefined(element, kind);
2109 }
2110
2111 TNode<Object> if_found_value = is_find_variant ? element : k;
2112 TNode<Number> next_k = NumberInc(k);
2113
2114 // The callback result states whether the desired element was found.
2115 TNode<Object> v =
2116 JSCall3(fncallback, this_arg, element, k, receiver,
2117 FindLoopAfterCallbackLazyFrameState(frame_state_params, next_k,
2118 if_found_value, variant));
2119
2120 GotoIf(ToBoolean(v), &out, if_found_value);
2121 });
2122
2123 // If the loop completed, the element was not found.
2124 TNode<Object> if_not_found_value =
2125 is_find_variant ? TNode<Object>::UncheckedCast(UndefinedConstant())
2126 : TNode<Object>::UncheckedCast(MinusOneConstant());
2127 Goto(&out, if_not_found_value);
2128
2129 Bind(&out);
2130 return out.PhiAt<Object>(0);
2131}
2132
2133namespace {
2134
2135struct EverySomeFrameStateParams {
2137 SharedFunctionInfoRef shared;
2138 TNode<Context> context;
2139 TNode<Object> target;
2145};
2146
2147FrameState EverySomeLoopLazyFrameState(const EverySomeFrameStateParams& params,
2148 TNode<Number> k,
2149 ArrayEverySomeVariant variant) {
2150 Builtin builtin = (variant == ArrayEverySomeVariant::kEvery)
2151 ? Builtin::kArrayEveryLoopLazyDeoptContinuation
2152 : Builtin::kArraySomeLoopLazyDeoptContinuation;
2153 Node* checkpoint_params[] = {params.receiver, params.callback,
2154 params.this_arg, k, params.original_length};
2156 params.jsgraph, params.shared, builtin, params.target, params.context,
2157 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
2159}
2160
2161FrameState EverySomeLoopEagerFrameState(const EverySomeFrameStateParams& params,
2162 TNode<Number> k,
2163 ArrayEverySomeVariant variant) {
2164 Builtin builtin = (variant == ArrayEverySomeVariant::kEvery)
2165 ? Builtin::kArrayEveryLoopEagerDeoptContinuation
2166 : Builtin::kArraySomeLoopEagerDeoptContinuation;
2167 Node* checkpoint_params[] = {params.receiver, params.callback,
2168 params.this_arg, k, params.original_length};
2170 params.jsgraph, params.shared, builtin, params.target, params.context,
2171 checkpoint_params, arraysize(checkpoint_params), params.outer_frame_state,
2173}
2174
2175} // namespace
2176
2177TNode<Boolean>
2179 MapInference* inference, const bool has_stability_dependency,
2183 TNode<Context> context = ContextInput();
2184 TNode<Object> target = TargetInput();
2186 TNode<Object> fncallback = ArgumentOrUndefined(0);
2188
2190
2191 EverySomeFrameStateParams frame_state_params{
2193 receiver, fncallback, this_arg, original_length};
2194
2196 fncallback,
2197 EverySomeLoopLazyFrameState(frame_state_params, ZeroConstant(), variant));
2198
2200
2202 Checkpoint(EverySomeLoopEagerFrameState(frame_state_params, k, variant));
2203 MaybeInsertMapChecks(inference, has_stability_dependency);
2204
2205 TNode<Object> element;
2206 std::tie(k, element) = SafeLoadElement(kind, receiver, k);
2207
2208 auto continue_label = MakeLabel();
2209 element = MaybeSkipHole(element, kind, &continue_label);
2210
2211 TNode<Object> v =
2212 JSCall3(fncallback, this_arg, element, k, receiver,
2213 EverySomeLoopLazyFrameState(frame_state_params, k, variant));
2214
2215 if (variant == ArrayEverySomeVariant::kEvery) {
2216 GotoIfNot(ToBoolean(v), &out, FalseConstant());
2217 } else {
2219 GotoIf(ToBoolean(v), &out, TrueConstant());
2220 }
2221 Goto(&continue_label);
2222 Bind(&continue_label);
2223 });
2224
2225 Goto(&out, (variant == ArrayEverySomeVariant::kEvery) ? TrueConstant()
2226 : FalseConstant());
2227
2228 Bind(&out);
2229 return out.PhiAt<Boolean>(0);
2230}
2231
2232namespace {
2233
2234Callable GetCallableForArrayIndexOfIncludes(ArrayIndexOfIncludesVariant variant,
2235 ElementsKind elements_kind,
2236 Isolate* isolate) {
2238 switch (elements_kind) {
2240 case HOLEY_SMI_ELEMENTS:
2241 return Builtins::CallableFor(isolate, Builtin::kArrayIndexOfSmi);
2242 case PACKED_ELEMENTS:
2243 case HOLEY_ELEMENTS:
2244 return Builtins::CallableFor(isolate,
2245 Builtin::kArrayIndexOfSmiOrObject);
2247 return Builtins::CallableFor(isolate,
2248 Builtin::kArrayIndexOfPackedDoubles);
2249 default:
2250 DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind);
2251 return Builtins::CallableFor(isolate,
2252 Builtin::kArrayIndexOfHoleyDoubles);
2253 }
2254 } else {
2256 switch (elements_kind) {
2258 case HOLEY_SMI_ELEMENTS:
2259 return Builtins::CallableFor(isolate, Builtin::kArrayIncludesSmi);
2260 case PACKED_ELEMENTS:
2261 case HOLEY_ELEMENTS:
2262 return Builtins::CallableFor(isolate,
2263 Builtin::kArrayIncludesSmiOrObject);
2265 return Builtins::CallableFor(isolate,
2266 Builtin::kArrayIncludesPackedDoubles);
2267 default:
2268 DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind);
2269 return Builtins::CallableFor(isolate,
2270 Builtin::kArrayIncludesHoleyDoubles);
2271 }
2272 }
2273 UNREACHABLE();
2274}
2275
2276} // namespace
2277TNode<Object>
2280 TNode<Context> context = ContextInput();
2282 TNode<Object> search_element = ArgumentOrUndefined(0);
2283 TNode<Object> from_index = ArgumentOrZero(1);
2284
2285 // TODO(jgruber): This currently only reduces to a stub call. Create a full
2286 // reduction (similar to other higher-order array builtins) instead of
2287 // lowering to a builtin call. E.g. Array.p.every and Array.p.some have almost
2288 // identical functionality.
2289
2292
2293 const bool have_from_index = ArgumentCount() > 1;
2294 if (have_from_index) {
2295 TNode<Smi> from_index_smi = CheckSmi(from_index);
2296
2297 // If the index is negative, it means the offset from the end and
2298 // therefore needs to be added to the length. If the result is still
2299 // negative, it needs to be clamped to 0.
2300 TNode<Boolean> cond = NumberLessThan(from_index_smi, ZeroConstant());
2301 from_index = SelectIf<Number>(cond)
2302 .Then(_ {
2303 return NumberMax(NumberAdd(length, from_index_smi),
2304 ZeroConstant());
2305 })
2306 .Else(_ { return from_index_smi; })
2307 .ExpectFalse()
2308 .Value();
2309 }
2310
2311 return Call4(GetCallableForArrayIndexOfIncludes(variant, kind, isolate()),
2312 context, elements, search_element, length, from_index);
2313}
2314namespace {
2315
2316struct PromiseCtorFrameStateParams {
2318 SharedFunctionInfoRef shared;
2320 TNode<Context> context;
2321 TNode<Object> target;
2322 FrameState outer_frame_state;
2323};
2324
2325// Remnant of old-style JSCallReducer code. Could be ported to graph assembler,
2326// but probably not worth the effort.
2327FrameState CreateConstructInvokeStubFrameState(
2328 Node* node, Node* outer_frame_state, SharedFunctionInfoRef shared,
2329 Node* context, CommonOperatorBuilder* common, TFGraph* graph) {
2330 const FrameStateFunctionInfo* state_info =
2331 common->CreateFrameStateFunctionInfo(FrameStateType::kConstructInvokeStub,
2332 1, 0, 0, shared.object(), {});
2333
2334 const Operator* op = common->FrameState(
2336 const Operator* op0 = common->StateValues(0, SparseInputMask::Dense());
2337 Node* node0 = graph->NewNode(op0);
2338
2339 static constexpr int kTargetInputIndex = 0;
2340 static constexpr int kReceiverInputIndex = 1;
2341 std::vector<Node*> params;
2342 params.push_back(node->InputAt(kReceiverInputIndex));
2343 const Operator* op_param = common->StateValues(
2344 static_cast<int>(params.size()), SparseInputMask::Dense());
2345 Node* params_node = graph->NewNode(op_param, static_cast<int>(params.size()),
2346 &params.front());
2347 DCHECK(context);
2348 return FrameState(graph->NewNode(op, params_node, node0, node0, context,
2349 node->InputAt(kTargetInputIndex),
2351}
2352
2353FrameState PromiseConstructorFrameState(
2354 const PromiseCtorFrameStateParams& params, CommonOperatorBuilder* common,
2355 TFGraph* graph) {
2356 DCHECK_EQ(1,
2357 params.shared.internal_formal_parameter_count_without_receiver());
2358 return CreateConstructInvokeStubFrameState(
2359 params.node_ptr, params.outer_frame_state, params.shared, params.context,
2360 common, graph);
2361}
2362
2363FrameState PromiseConstructorLazyFrameState(
2364 const PromiseCtorFrameStateParams& params,
2365 FrameState constructor_frame_state) {
2366 // The deopt continuation of this frame state is never called; the frame state
2367 // is only necessary to obtain the right stack trace.
2368 JSGraph* jsgraph = params.jsgraph;
2369 Node* checkpoint_params[] = {
2370 jsgraph->UndefinedConstant(), /* receiver */
2371 jsgraph->UndefinedConstant(), /* promise */
2372 jsgraph->UndefinedConstant(), /* reject function */
2373 jsgraph->TheHoleConstant() /* exception */
2374 };
2376 jsgraph, params.shared, Builtin::kPromiseConstructorLazyDeoptContinuation,
2377 params.target, params.context, checkpoint_params,
2378 arraysize(checkpoint_params), constructor_frame_state,
2380}
2381
2382FrameState PromiseConstructorLazyWithCatchFrameState(
2383 const PromiseCtorFrameStateParams& params,
2384 FrameState constructor_frame_state, TNode<JSPromise> promise,
2385 TNode<JSFunction> reject) {
2386 // This continuation just returns the created promise and takes care of
2387 // exceptions thrown by the executor.
2388 Node* checkpoint_params[] = {
2389 params.jsgraph->UndefinedConstant(), /* receiver */
2390 promise, reject};
2392 params.jsgraph, params.shared,
2393 Builtin::kPromiseConstructorLazyDeoptContinuation, params.target,
2394 params.context, checkpoint_params, arraysize(checkpoint_params),
2395 constructor_frame_state, ContinuationFrameStateMode::LAZY_WITH_CATCH);
2396}
2397
2398} // namespace
2399
2403
2406 TNode<Context> context = ContextInput();
2407 TNode<Object> target = TargetInput();
2408 TNode<Object> executor = n.Argument(0);
2409 DCHECK_EQ(target, NewTargetInput());
2410
2411 SharedFunctionInfoRef promise_shared =
2412 native_context.promise_function(broker()).shared(broker());
2413
2414 PromiseCtorFrameStateParams frame_state_params{jsgraph(), promise_shared,
2415 node_ptr(), context,
2417
2418 // Insert a construct stub frame into the chain of frame states. This will
2419 // reconstruct the proper frame when deoptimizing within the constructor.
2420 // For the frame state, we only provide the executor parameter, even if more
2421 // arguments were passed. This is not observable from JS.
2422 FrameState constructor_frame_state =
2423 PromiseConstructorFrameState(frame_state_params, common(), graph());
2424
2425 IfNot(ObjectIsCallable(executor))
2426 .Then((_ {
2428 Runtime::kThrowTypeError,
2430 Smi::FromEnum(MessageTemplate::kResolverNotAFunction).value()),
2431 executor, context,
2432 PromiseConstructorLazyFrameState(frame_state_params,
2433 constructor_frame_state));
2434 }))
2435 .ExpectTrue();
2436
2437 TNode<JSPromise> promise = CreatePromise(context);
2438
2439 // 8. CreatePromiseResolvingFunctions
2440 // Allocate a promise context for the closures below.
2441 TNode<Context> promise_context = CreateFunctionContext(
2443 StoreContextSlot(promise_context, PromiseBuiltins::kPromiseSlot, promise);
2445 FalseConstant());
2447 TrueConstant());
2448
2449 // Allocate closures for the resolve and reject cases.
2450 SharedFunctionInfoRef resolve_sfi =
2451 MakeRef(broker(), broker()
2452 ->isolate()
2453 ->factory()
2454 ->promise_capability_default_resolve_shared_fun());
2455 TNode<JSFunction> resolve =
2456 CreateClosureFromBuiltinSharedFunctionInfo(resolve_sfi, promise_context);
2457
2458 SharedFunctionInfoRef reject_sfi =
2459 MakeRef(broker(), broker()
2460 ->isolate()
2461 ->factory()
2462 ->promise_capability_default_reject_shared_fun());
2463 TNode<JSFunction> reject =
2464 CreateClosureFromBuiltinSharedFunctionInfo(reject_sfi, promise_context);
2465
2466 FrameState lazy_with_catch_frame_state =
2467 PromiseConstructorLazyWithCatchFrameState(
2468 frame_state_params, constructor_frame_state, promise, reject);
2469
2470 // 9. Call executor with both resolving functions.
2471 // 10a. Call reject if the call to executor threw.
2472 Try(_ {
2473 CallPromiseExecutor(executor, resolve, reject, lazy_with_catch_frame_state);
2474 }).Catch([&](TNode<Object> exception) {
2475 // Clear pending message since the exception is not going to be rethrown.
2477 CallPromiseReject(reject, exception, lazy_with_catch_frame_state);
2478 });
2479
2480 return promise;
2481}
2482
2483#undef _
2484
2486 JSCallReducerAssembler* gasm) {
2487 auto catch_scope = gasm->catch_scope();
2488 DCHECK(catch_scope->is_outermost());
2489
2490 if (catch_scope->has_handler() &&
2491 catch_scope->has_exceptional_control_flow()) {
2492 TNode<Object> handler_exception;
2493 Effect handler_effect{nullptr};
2494 Control handler_control{nullptr};
2496 &handler_exception, &handler_effect, &handler_control);
2497
2498 ReplaceWithValue(gasm->outermost_handler(), handler_exception,
2499 handler_effect, handler_control);
2500 }
2501
2502 return {gasm->effect(), gasm->control()};
2503}
2504
2506 Node* subgraph) {
2507 // TODO(jgruber): Consider a less fiddly way of integrating the new subgraph
2508 // into the outer graph. For instance, the subgraph could be created in
2509 // complete isolation, and then plugged into the outer graph in one go.
2510 // Instead of manually tracking IfException nodes, we could iterate the
2511 // subgraph.
2512
2513 // Replace the Call node with the newly-produced subgraph.
2514 ReplaceWithValue(gasm->node_ptr(), subgraph, gasm->effect(), gasm->control());
2515
2516 // Wire exception edges contained in the newly-produced subgraph into the
2517 // outer graph.
2518 auto catch_scope = gasm->catch_scope();
2519 DCHECK(catch_scope->is_outermost());
2520
2521 if (catch_scope->has_handler() &&
2522 catch_scope->has_exceptional_control_flow()) {
2523 TNode<Object> handler_exception;
2524 Effect handler_effect{nullptr};
2525 Control handler_control{nullptr};
2527 &handler_exception, &handler_effect, &handler_control);
2528
2529 ReplaceWithValue(gasm->outermost_handler(), handler_exception,
2530 handler_effect, handler_control);
2531 }
2532
2533 return Replace(subgraph);
2534}
2535
2537 JSCallNode n(node);
2538 CallParameters const& p = n.Parameters();
2540 return NoChange();
2541 }
2542 if (n.ArgumentCount() < 1) {
2543 Node* value = jsgraph()->NaNConstant();
2544 ReplaceWithValue(node, value);
2545 return Replace(value);
2546 }
2547
2548 JSCallReducerAssembler a(this, node);
2549 Node* subgraph = a.ReduceMathUnary(op);
2550 return ReplaceWithSubgraph(&a, subgraph);
2551}
2552
2554 JSCallNode n(node);
2555 CallParameters const& p = n.Parameters();
2557 return NoChange();
2558 }
2559 if (n.ArgumentCount() < 1) {
2560 Node* value = jsgraph()->NaNConstant();
2561 ReplaceWithValue(node, value);
2562 return Replace(value);
2563 }
2564
2565 JSCallReducerAssembler a(this, node);
2566 Node* subgraph = a.ReduceMathBinary(op);
2567 return ReplaceWithSubgraph(&a, subgraph);
2568}
2569
2570// ES6 section 20.2.2.19 Math.imul ( x, y )
2572 JSCallNode n(node);
2573 CallParameters const& p = n.Parameters();
2575 return NoChange();
2576 }
2577 if (n.ArgumentCount() < 1) {
2578 Node* value = jsgraph()->ZeroConstant();
2579 ReplaceWithValue(node, value);
2580 return Replace(value);
2581 }
2582 Node* left = n.Argument(0);
2583 Node* right = n.ArgumentOr(1, jsgraph()->ZeroConstant());
2584 Effect effect = n.effect();
2585 Control control = n.control();
2586
2587 left = effect =
2588 graph()->NewNode(simplified()->SpeculativeToNumber(
2590 left, effect, control);
2591 right = effect =
2592 graph()->NewNode(simplified()->SpeculativeToNumber(
2594 right, effect, control);
2595 left = graph()->NewNode(simplified()->NumberToUint32(), left);
2596 right = graph()->NewNode(simplified()->NumberToUint32(), right);
2597 Node* value = graph()->NewNode(simplified()->NumberImul(), left, right);
2598 ReplaceWithValue(node, value, effect);
2599 return Replace(value);
2600}
2601
2602// ES6 section 20.2.2.11 Math.clz32 ( x )
2604 JSCallNode n(node);
2605 CallParameters const& p = n.Parameters();
2607 return NoChange();
2608 }
2609 if (n.ArgumentCount() < 1) {
2610 Node* value = jsgraph()->ConstantNoHole(32);
2611 ReplaceWithValue(node, value);
2612 return Replace(value);
2613 }
2614 Node* input = n.Argument(0);
2615 Effect effect = n.effect();
2616 Control control = n.control();
2617
2618 input = effect =
2619 graph()->NewNode(simplified()->SpeculativeToNumber(
2621 input, effect, control);
2622 input = graph()->NewNode(simplified()->NumberToUint32(), input);
2623 Node* value = graph()->NewNode(simplified()->NumberClz32(), input);
2624 ReplaceWithValue(node, value, effect);
2625 return Replace(value);
2626}
2627
2628// ES6 section 20.2.2.24 Math.max ( value1, value2, ...values )
2629// ES6 section 20.2.2.25 Math.min ( value1, value2, ...values )
2631 Node* empty_value) {
2632 JSCallNode n(node);
2633 CallParameters const& p = n.Parameters();
2635 return NoChange();
2636 }
2637 if (n.ArgumentCount() < 1) {
2638 ReplaceWithValue(node, empty_value);
2639 return Replace(empty_value);
2640 }
2641 Node* effect = NodeProperties::GetEffectInput(node);
2642 Node* control = NodeProperties::GetControlInput(node);
2643
2644 Node* value = effect =
2645 graph()->NewNode(simplified()->SpeculativeToNumber(
2647 n.Argument(0), effect, control);
2648 for (int i = 1; i < n.ArgumentCount(); i++) {
2649 Node* input = effect = graph()->NewNode(
2651 p.feedback()),
2652 n.Argument(i), effect, control);
2653 value = graph()->NewNode(op, value, input);
2654 }
2655
2656 ReplaceWithValue(node, value, effect);
2657 return Replace(value);
2658}
2659
2661 switch (node->opcode()) {
2662 case IrOpcode::kJSConstruct:
2663 return ReduceJSConstruct(node);
2664 case IrOpcode::kJSConstructWithArrayLike:
2665 return ReduceJSConstructWithArrayLike(node);
2666 case IrOpcode::kJSConstructWithSpread:
2667 return ReduceJSConstructWithSpread(node);
2668 case IrOpcode::kJSConstructForwardAllArgs:
2670 case IrOpcode::kJSCall:
2671 return ReduceJSCall(node);
2672 case IrOpcode::kJSCallWithArrayLike:
2673 return ReduceJSCallWithArrayLike(node);
2674 case IrOpcode::kJSCallWithSpread:
2675 return ReduceJSCallWithSpread(node);
2676 default:
2677 break;
2678 }
2679 return NoChange();
2680}
2681
2683 // TODO(turbofan): This is not the best solution; ideally we would be able
2684 // to teach the GraphReducer about arbitrary dependencies between different
2685 // nodes, even if they don't show up in the use list of the other node.
2686 std::set<Node*> const waitlist = std::move(waitlist_);
2687 for (Node* node : waitlist) {
2688 if (!node->IsDead()) {
2689 // Remember the max node id before reduction.
2690 NodeId const max_id = static_cast<NodeId>(graph()->NodeCount() - 1);
2691 Reduction const reduction = Reduce(node);
2692 if (reduction.Changed()) {
2693 Node* replacement = reduction.replacement();
2694 if (replacement != node) {
2695 Replace(node, replacement, max_id);
2696 }
2697 }
2698 }
2699 }
2700}
2701
2702// ES6 section 22.1.1 The Array Constructor
2704 JSCallNode n(node);
2705 Node* target = n.target();
2706 CallParameters const& p = n.Parameters();
2707
2708 // Turn the {node} into a {JSCreateArray} call.
2709 size_t const arity = p.arity_without_implicit_args();
2710 node->RemoveInput(n.FeedbackVectorIndex());
2711 NodeProperties::ReplaceValueInput(node, target, 0);
2712 NodeProperties::ReplaceValueInput(node, target, 1);
2714 javascript()->CreateArray(arity, std::nullopt));
2715 return Changed(node);
2716}
2717
2718// ES6 section 19.3.1.1 Boolean ( value )
2720 // Replace the {node} with a proper {ToBoolean} operator.
2721 JSCallNode n(node);
2722 Node* value = n.ArgumentOrUndefined(0, jsgraph());
2723 value = graph()->NewNode(simplified()->ToBoolean(), value);
2724 ReplaceWithValue(node, value);
2725 return Replace(value);
2726}
2727
2728// ES section #sec-object-constructor
2730 JSCallNode n(node);
2731 if (n.ArgumentCount() < 1) return NoChange();
2732 Node* value = n.Argument(0);
2733 Effect effect = n.effect();
2734
2735 // We can fold away the Tagged<Object>(x) call if |x| is definitely not a
2736 // primitive.
2737 if (NodeProperties::CanBePrimitive(broker(), value, effect)) {
2738 if (!NodeProperties::CanBeNullOrUndefined(broker(), value, effect)) {
2739 // Turn the {node} into a {JSToObject} call if we know that
2740 // the {value} cannot be null or undefined.
2742 NodeProperties::ChangeOp(node, javascript()->ToObject());
2743 return Changed(node);
2744 }
2745 } else {
2746 ReplaceWithValue(node, value);
2747 return Replace(value);
2748 }
2749 return NoChange();
2750}
2751
2752// ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
2754 JSCallNode n(node);
2755 CallParameters const& p = n.Parameters();
2756 CallFeedbackRelation new_feedback_relation =
2760 int arity = p.arity_without_implicit_args();
2761
2762 if (arity < 2) {
2763 // Degenerate cases.
2764 ConvertReceiverMode convert_mode;
2765 if (arity == 0) {
2766 // Neither thisArg nor argArray was provided.
2768 node->ReplaceInput(n.TargetIndex(), n.receiver());
2769 node->ReplaceInput(n.ReceiverIndex(), jsgraph()->UndefinedConstant());
2770 } else {
2771 DCHECK_EQ(arity, 1);
2772 // The argArray was not provided, just remove the {target}.
2773 convert_mode = ConvertReceiverMode::kAny;
2774 node->RemoveInput(n.TargetIndex());
2775 --arity;
2776 }
2777 // Change {node} to a {JSCall} and try to reduce further.
2780 p.feedback(), convert_mode,
2781 p.speculation_mode(), new_feedback_relation));
2782 return Changed(node).FollowedBy(ReduceJSCall(node));
2783 }
2784
2785 // Turn the JSCall into a JSCallWithArrayLike.
2786 // If {argArray} can be null or undefined, we have to generate branches since
2787 // JSCallWithArrayLike would throw for null or undefined.
2788
2789 Node* target = n.receiver();
2790 Node* this_argument = n.Argument(0);
2791 Node* arguments_list = n.Argument(1);
2792 Node* context = n.context();
2793 FrameState frame_state = n.frame_state();
2794 Effect effect = n.effect();
2795 Control control = n.control();
2796
2797 // If {arguments_list} cannot be null or undefined, we don't need
2798 // to expand this {node} to control-flow.
2799 if (!NodeProperties::CanBeNullOrUndefined(broker(), arguments_list, effect)) {
2800 // Massage the value inputs appropriately.
2801 node->ReplaceInput(n.TargetIndex(), target);
2802 node->ReplaceInput(n.ReceiverIndex(), this_argument);
2803 node->ReplaceInput(n.ArgumentIndex(0), arguments_list);
2804 while (arity-- > 1) node->RemoveInput(n.ArgumentIndex(1));
2805
2806 // Morph the {node} to a {JSCallWithArrayLike}.
2808 node, javascript()->CallWithArrayLike(p.frequency(), p.feedback(),
2809 p.speculation_mode(),
2810 new_feedback_relation));
2811 return Changed(node).FollowedBy(ReduceJSCallWithArrayLike(node));
2812 }
2813
2814 // Check whether {arguments_list} is null.
2815 Node* check_null =
2816 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list,
2817 jsgraph()->NullConstant());
2818 control = graph()->NewNode(common()->Branch(BranchHint::kFalse), check_null,
2819 control);
2820 Node* if_null = graph()->NewNode(common()->IfTrue(), control);
2821 control = graph()->NewNode(common()->IfFalse(), control);
2822
2823 // Check whether {arguments_list} is undefined.
2824 Node* check_undefined =
2825 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list,
2826 jsgraph()->UndefinedConstant());
2827 control = graph()->NewNode(common()->Branch(BranchHint::kFalse),
2828 check_undefined, control);
2829 Node* if_undefined = graph()->NewNode(common()->IfTrue(), control);
2830 control = graph()->NewNode(common()->IfFalse(), control);
2831
2832 // Lower to {JSCallWithArrayLike} if {arguments_list} is neither null
2833 // nor undefined.
2834 Node* effect0 = effect;
2835 Node* control0 = control;
2836 Node* value0 = effect0 = control0 = graph()->NewNode(
2837 javascript()->CallWithArrayLike(p.frequency(), p.feedback(),
2838 p.speculation_mode(),
2839 new_feedback_relation),
2840 target, this_argument, arguments_list, n.feedback_vector(), context,
2841 frame_state, effect0, control0);
2842
2843 // Lower to {JSCall} if {arguments_list} is either null or undefined.
2844 Node* effect1 = effect;
2845 Node* control1 = graph()->NewNode(common()->Merge(2), if_null, if_undefined);
2846 Node* value1 = effect1 = control1 = graph()->NewNode(
2847 javascript()->Call(JSCallNode::ArityForArgc(0)), target, this_argument,
2848 n.feedback_vector(), context, frame_state, effect1, control1);
2849
2850 // Rewire potential exception edges.
2851 Node* if_exception = nullptr;
2852 if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
2853 // Create appropriate {IfException} and {IfSuccess} nodes.
2854 Node* if_exception0 =
2855 graph()->NewNode(common()->IfException(), control0, effect0);
2856 control0 = graph()->NewNode(common()->IfSuccess(), control0);
2857 Node* if_exception1 =
2858 graph()->NewNode(common()->IfException(), control1, effect1);
2859 control1 = graph()->NewNode(common()->IfSuccess(), control1);
2860
2861 // Join the exception edges.
2862 Node* merge =
2863 graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
2864 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
2865 if_exception1, merge);
2866 Node* phi =
2868 if_exception0, if_exception1, merge);
2869 ReplaceWithValue(if_exception, phi, ephi, merge);
2870 }
2871
2872 // Join control paths.
2873 control = graph()->NewNode(common()->Merge(2), control0, control1);
2874 effect = graph()->NewNode(common()->EffectPhi(2), effect0, effect1, control);
2875 Node* value =
2876 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), value0,
2877 value1, control);
2878 ReplaceWithValue(node, value, effect, control);
2879 return Replace(value);
2880}
2881
2882// ES section #sec-function.prototype.bind
2884 JSCallNode n(node);
2885 CallParameters const& p = n.Parameters();
2887 return NoChange();
2888 }
2889
2890 // Value inputs to the {node} are as follows:
2891 //
2892 // - target, which is Function.prototype.bind JSFunction
2893 // - receiver, which is the [[BoundTargetFunction]]
2894 // - bound_this (optional), which is the [[BoundThis]]
2895 // - and all the remaining value inputs are [[BoundArguments]]
2896 Node* receiver = n.receiver();
2897 Node* context = n.context();
2898 Effect effect = n.effect();
2899 Control control = n.control();
2900
2901 // Ensure that the {receiver} is known to be a JSBoundFunction or
2902 // a JSFunction with the same [[Prototype]], and all maps we've
2903 // seen for the {receiver} so far indicate that {receiver} is
2904 // definitely a constructor or not a constructor.
2905 MapInference inference(broker(), receiver, effect);
2906 if (!inference.HaveMaps()) return NoChange();
2907 ZoneRefSet<Map> const& receiver_maps = inference.GetMaps();
2908
2909 MapRef first_receiver_map = receiver_maps[0];
2910 bool const is_constructor = first_receiver_map.is_constructor();
2911
2912 HeapObjectRef prototype = first_receiver_map.prototype(broker());
2913
2914 for (MapRef receiver_map : receiver_maps) {
2915 HeapObjectRef map_prototype = receiver_map.prototype(broker());
2916
2917 // Check for consistency among the {receiver_maps}.
2918 if (!map_prototype.equals(prototype) ||
2919 receiver_map.is_constructor() != is_constructor ||
2920 !InstanceTypeChecker::IsJSFunctionOrBoundFunctionOrWrappedFunction(
2921 receiver_map.instance_type())) {
2922 return inference.NoChange();
2923 }
2924
2925 // Disallow binding of slow-mode functions. We need to figure out
2926 // whether the length and name property are in the original state.
2927 if (receiver_map.is_dictionary_map()) return inference.NoChange();
2928
2929 // Check whether the length and name properties are still present
2930 // as AccessorInfo objects. In that case, their values can be
2931 // recomputed even if the actual value of the object changes.
2932 // This mirrors the checks done in builtins-function-gen.cc at
2933 // runtime otherwise.
2934 int minimum_nof_descriptors =
2935 std::max(
2937 JSFunctionOrBoundFunctionOrWrappedFunction::
2938 kNameDescriptorIndex}) +
2939 1;
2940 if (receiver_map.NumberOfOwnDescriptors() < minimum_nof_descriptors) {
2941 return inference.NoChange();
2942 }
2943 const InternalIndex kLengthIndex(
2945 const InternalIndex kNameIndex(
2947 StringRef length_string = broker()->length_string();
2948 StringRef name_string = broker()->name_string();
2949
2950 OptionalObjectRef length_value(
2951 receiver_map.GetStrongValue(broker(), kLengthIndex));
2952 OptionalObjectRef name_value(
2953 receiver_map.GetStrongValue(broker(), kNameIndex));
2954 if (!length_value || !name_value) {
2956 broker(), "name or length descriptors on map " << receiver_map);
2957 return inference.NoChange();
2958 }
2959 if (!receiver_map.GetPropertyKey(broker(), kLengthIndex)
2960 .equals(length_string) ||
2961 !length_value->IsAccessorInfo() ||
2962 !receiver_map.GetPropertyKey(broker(), kNameIndex)
2963 .equals(name_string) ||
2964 !name_value->IsAccessorInfo()) {
2965 return inference.NoChange();
2966 }
2967 }
2968
2969 // Choose the map for the resulting JSBoundFunction (but bail out in case of a
2970 // custom prototype).
2971 MapRef map =
2973 ? native_context().bound_function_with_constructor_map(broker())
2974 : native_context().bound_function_without_constructor_map(broker());
2975 if (!map.prototype(broker()).equals(prototype)) return inference.NoChange();
2976
2977 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
2978 control, p.feedback());
2979
2980 // Replace the {node} with a JSCreateBoundFunction.
2981 static constexpr int kBoundThis = 1;
2982 static constexpr int kReceiverContextEffectAndControl = 4;
2983 int const arity = n.ArgumentCount();
2984
2985 if (arity > 0) {
2986 MapRef fixed_array_map = broker()->fixed_array_map();
2987 AllocationBuilder ab(jsgraph(), broker(), effect, control);
2988 if (!ab.CanAllocateArray(arity, fixed_array_map)) {
2989 return NoChange();
2990 }
2991 }
2992
2993 int const arity_with_bound_this = std::max(arity, kBoundThis);
2994 int const input_count =
2995 arity_with_bound_this + kReceiverContextEffectAndControl;
2996 Node** inputs = graph()->zone()->AllocateArray<Node*>(input_count);
2997 int cursor = 0;
2998 inputs[cursor++] = receiver;
2999 inputs[cursor++] = n.ArgumentOrUndefined(0, jsgraph()); // bound_this.
3000 for (int i = 1; i < arity; ++i) {
3001 inputs[cursor++] = n.Argument(i);
3002 }
3003 inputs[cursor++] = context;
3004 inputs[cursor++] = effect;
3005 inputs[cursor++] = control;
3006 DCHECK_EQ(cursor, input_count);
3007 Node* value = effect =
3008 graph()->NewNode(javascript()->CreateBoundFunction(
3009 arity_with_bound_this - kBoundThis, map),
3010 input_count, inputs);
3011 ReplaceWithValue(node, value, effect, control);
3012 return Replace(value);
3013}
3014
3015// ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
3017 JSCallNode n(node);
3018 CallParameters const& p = n.Parameters();
3019 Node* target = n.target();
3020 Effect effect = n.effect();
3021 Control control = n.control();
3022
3023 // Change context of {node} to the Function.prototype.call context,
3024 // to ensure any exception is thrown in the correct context.
3025 Node* context;
3026 HeapObjectMatcher m(target);
3027 if (m.HasResolvedValue() && m.Ref(broker()).IsJSFunction()) {
3028 JSFunctionRef function = m.Ref(broker()).AsJSFunction();
3029 context = jsgraph()->ConstantNoHole(function.context(broker()), broker());
3030 } else {
3031 context = effect = graph()->NewNode(
3032 simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
3033 effect, control);
3034 }
3037
3038 // Remove the target from {node} and use the receiver as target instead, and
3039 // the thisArg becomes the new target. If thisArg was not provided, insert
3040 // undefined instead.
3041 int arity = p.arity_without_implicit_args();
3042 ConvertReceiverMode convert_mode;
3043 if (arity == 0) {
3044 // The thisArg was not provided, use undefined as receiver.
3046 node->ReplaceInput(n.TargetIndex(), n.receiver());
3047 node->ReplaceInput(n.ReceiverIndex(), jsgraph()->UndefinedConstant());
3048 } else {
3049 // Just remove the target, which is the first value input.
3050 convert_mode = ConvertReceiverMode::kAny;
3051 node->RemoveInput(n.TargetIndex());
3052 --arity;
3053 }
3056 p.feedback(), convert_mode, p.speculation_mode(),
3058 // Try to further reduce the JSCall {node}.
3059 return Changed(node).FollowedBy(ReduceJSCall(node));
3060}
3061
3062// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V)
3064 JSCallNode n(node);
3065 Node* receiver = n.receiver();
3066 Node* object = n.ArgumentOrUndefined(0, jsgraph());
3067 Node* context = n.context();
3068 FrameState frame_state = n.frame_state();
3069 Effect effect = n.effect();
3070 Control control = n.control();
3071
3072 // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the
3073 // stack trace doesn't contain the @@hasInstance call; we have the
3074 // corresponding bug in the baseline case. Some massaging of the frame
3075 // state would be necessary here.
3076
3077 // Morph this {node} into a JSOrdinaryHasInstance node.
3078 node->ReplaceInput(0, receiver);
3079 node->ReplaceInput(1, object);
3080 node->ReplaceInput(2, context);
3081 node->ReplaceInput(3, frame_state);
3082 node->ReplaceInput(4, effect);
3083 node->ReplaceInput(5, control);
3084 node->TrimInputCount(6);
3085 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
3086 return Changed(node);
3087}
3088
3091
3092 // Try to determine the {object} map.
3093 MapInference inference(broker(), object, effect);
3094 if (!inference.HaveMaps()) return NoChange();
3095 ZoneRefSet<Map> const& object_maps = inference.GetMaps();
3096
3097 MapRef candidate_map = object_maps[0];
3098 HeapObjectRef candidate_prototype = candidate_map.prototype(broker());
3099
3100 // Check if we can constant-fold the {candidate_prototype}.
3101 for (size_t i = 0; i < object_maps.size(); ++i) {
3102 MapRef object_map = object_maps[i];
3103 HeapObjectRef map_prototype = object_map.prototype(broker());
3104 if (IsSpecialReceiverInstanceType(object_map.instance_type()) ||
3105 !map_prototype.equals(candidate_prototype)) {
3106 // We exclude special receivers, like JSProxy or API objects that
3107 // might require access checks here; we also don't want to deal
3108 // with hidden prototypes at this point.
3109 return inference.NoChange();
3110 }
3111 // The above check also excludes maps for primitive values, which is
3112 // important because we are not applying [[ToObject]] here as expected.
3113 DCHECK(!object_map.IsPrimitiveMap() && object_map.IsJSReceiverMap());
3114 }
3115 if (!inference.RelyOnMapsViaStability(dependencies())) {
3116 return inference.NoChange();
3117 }
3118 Node* value = jsgraph()->ConstantNoHole(candidate_prototype, broker());
3119 ReplaceWithValue(node, value);
3120 return Replace(value);
3121}
3122
3123// ES6 section 19.1.2.11 Object.getPrototypeOf ( O )
3125 JSCallNode n(node);
3126 Node* object = n.ArgumentOrUndefined(0, jsgraph());
3127 return ReduceObjectGetPrototype(node, object);
3128}
3129
3130// ES section #sec-object.is
3132 JSCallNode n(node);
3133 Node* lhs = n.ArgumentOrUndefined(0, jsgraph());
3134 Node* rhs = n.ArgumentOrUndefined(1, jsgraph());
3135 Node* value = graph()->NewNode(simplified()->SameValue(), lhs, rhs);
3136 ReplaceWithValue(node, value);
3137 return Replace(value);
3138}
3139
3140// ES6 section B.2.2.1.1 get Object.prototype.__proto__
3142 JSCallNode n(node);
3143 return ReduceObjectGetPrototype(node, n.receiver());
3144}
3145
3146// ES #sec-object.prototype.hasownproperty
3148 JSCallNode call_node(node);
3149 Node* receiver = call_node.receiver();
3150 Node* name = call_node.ArgumentOrUndefined(0, jsgraph());
3151 Effect effect = call_node.effect();
3152 Control control = call_node.control();
3153
3154 // We can optimize a call to Object.prototype.hasOwnProperty if it's being
3155 // used inside a fast-mode for..in, so for code like this:
3156 //
3157 // for (name in receiver) {
3158 // if (receiver.hasOwnProperty(name)) {
3159 // ...
3160 // }
3161 // }
3162 //
3163 // If the for..in is in fast-mode, we know that the {receiver} has {name}
3164 // as own property, otherwise the enumeration wouldn't include it. The graph
3165 // constructed by the BytecodeGraphBuilder in this case looks like this:
3166
3167 // receiver
3168 // ^ ^
3169 // | |
3170 // | +-+
3171 // | |
3172 // | JSToObject
3173 // | ^
3174 // | |
3175 // | JSForInNext
3176 // | ^
3177 // +----+ |
3178 // | |
3179 // JSCall[hasOwnProperty]
3180
3181 // We can constant-fold the {node} to True in this case, and insert
3182 // a (potentially redundant) map check to guard the fact that the
3183 // {receiver} map didn't change since the dominating JSForInNext. This
3184 // map check is only necessary when TurboFan cannot prove that there
3185 // is no observable side effect between the {JSForInNext} and the
3186 // {JSCall} to Object.prototype.hasOwnProperty.
3187 //
3188 // Also note that it's safe to look through the {JSToObject}, since the
3189 // Object.prototype.hasOwnProperty does an implicit ToObject anyway, and
3190 // these operations are not observable.
3191 if (name->opcode() == IrOpcode::kJSForInNext) {
3192 JSForInNextNode n(name);
3193 if (n.Parameters().mode() != ForInMode::kGeneric) {
3194 Node* object = n.receiver();
3195 Node* cache_type = n.cache_type();
3196 if (object->opcode() == IrOpcode::kJSToObject) {
3197 object = NodeProperties::GetValueInput(object, 0);
3198 }
3199 if (object == receiver) {
3200 // No need to repeat the map check if we can prove that there's no
3201 // observable side effect between {effect} and {name].
3203 Node* receiver_map = effect =
3204 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
3205 receiver, effect, control);
3206 Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
3207 receiver_map, cache_type);
3208 effect = graph()->NewNode(
3209 simplified()->CheckIf(DeoptimizeReason::kWrongMap), check, effect,
3210 control);
3211 }
3212 Node* value = jsgraph()->TrueConstant();
3213 ReplaceWithValue(node, value, effect, control);
3214 return Replace(value);
3215 }
3216
3217 // We can also optimize for this case below:
3218
3219 // receiver(is a heap constant with fast map)
3220 // ^
3221 // | object(all keys are enumerable)
3222 // | ^
3223 // | |
3224 // | JSForInNext
3225 // | ^
3226 // +----+ |
3227 // | |
3228 // JSCall[hasOwnProperty]
3229
3230 // We can replace the {JSCall} with several internalized string
3231 // comparisons.
3232
3233 if (receiver->opcode() == IrOpcode::kHeapConstant) {
3234 MapInference inference(broker(), receiver, effect);
3235 if (!inference.HaveMaps()) {
3236 return inference.NoChange();
3237 }
3238 const ZoneRefSet<Map>& receiver_maps = inference.GetMaps();
3239 if (receiver_maps.size() == 1) {
3240 const MapRef receiver_map = *receiver_maps.begin();
3241 InstanceType instance_type = receiver_map.instance_type();
3242 int const nof = receiver_map.NumberOfOwnDescriptors();
3243 // We set a heuristic value to limit the compare instructions number.
3244 if (nof > 4 || instance_type <= LAST_SPECIAL_RECEIVER_TYPE ||
3245 receiver_map.is_dictionary_map()) {
3246 return inference.NoChange();
3247 }
3248 // Replace builtin call with several internalized string comparisons.
3249 CallParameters const& p = call_node.Parameters();
3251 &effect, control, p.feedback());
3252#define __ gasm.
3253 JSGraphAssembler gasm(broker(), jsgraph(), jsgraph()->zone(),
3255 gasm.InitializeEffectControl(effect, control);
3256 auto done = __ MakeLabel(MachineRepresentation::kTagged);
3257 const DescriptorArrayRef descriptor_array =
3258 receiver_map.instance_descriptors(broker());
3259 for (InternalIndex key_index : InternalIndex::Range(nof)) {
3260 NameRef receiver_key =
3261 descriptor_array.GetPropertyKey(broker(), key_index);
3262 Node* lhs = jsgraph()->HeapConstantNoHole(receiver_key.object());
3263 __ GotoIf(__ ReferenceEqual(TNode<Object>::UncheckedCast(lhs),
3265 &done, __ TrueConstant());
3266 }
3267 __ Goto(&done, __ FalseConstant());
3268 __ Bind(&done);
3269
3270 Node* value = done.PhiAt(0);
3271 ReplaceWithValue(node, value, gasm.effect(), gasm.control());
3272 return Replace(value);
3273#undef __
3274 }
3275 return inference.NoChange();
3276 }
3277 }
3278 }
3279
3280 return NoChange();
3281}
3282
3283// ES #sec-object.prototype.isprototypeof
3285 JSCallNode n(node);
3286 Node* receiver = n.receiver();
3287 Node* value = n.ArgumentOrUndefined(0, jsgraph());
3288 Effect effect = n.effect();
3289
3290 // Ensure that the {receiver} is known to be a JSReceiver (so that
3291 // the ToObject step of Object.prototype.isPrototypeOf is a no-op).
3292 MapInference inference(broker(), receiver, effect);
3293 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAreJSReceiver()) {
3294 return NoChange();
3295 }
3296
3297 // We don't check whether {value} is a proper JSReceiver here explicitly,
3298 // and don't explicitly rule out Primitive {value}s, since all of them
3299 // have null as their prototype, so the prototype chain walk inside the
3300 // JSHasInPrototypeChain operator immediately aborts and yields false.
3301 NodeProperties::ReplaceValueInput(node, value, n.TargetIndex());
3302 for (int i = node->op()->ValueInputCount(); i > 2; i--) {
3303 node->RemoveInput(2);
3304 }
3305 NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain());
3306 return Changed(node);
3307}
3308
3309// ES6 section 26.1.1 Reflect.apply ( target, thisArgument, argumentsList )
3311 JSCallNode n(node);
3312 CallParameters const& p = n.Parameters();
3313 int arity = p.arity_without_implicit_args();
3314 // Massage value inputs appropriately.
3315 static_assert(n.ReceiverIndex() > n.TargetIndex());
3316 node->RemoveInput(n.ReceiverIndex());
3317 node->RemoveInput(n.TargetIndex());
3318 while (arity < 3) {
3319 node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant());
3320 }
3321 while (arity-- > 3) {
3322 node->RemoveInput(arity);
3323 }
3325 node, javascript()->CallWithArrayLike(p.frequency(), p.feedback(),
3326 p.speculation_mode(),
3328 return Changed(node).FollowedBy(ReduceJSCallWithArrayLike(node));
3329}
3330
3331// ES6 section 26.1.2 Reflect.construct ( target, argumentsList [, newTarget] )
3333 JSCallNode n(node);
3334 CallParameters const& p = n.Parameters();
3335 int arity = p.arity_without_implicit_args();
3336 // Massage value inputs appropriately.
3337 TNode<Object> arg_target = n.ArgumentOrUndefined(0, jsgraph());
3338 TNode<Object> arg_argument_list = n.ArgumentOrUndefined(1, jsgraph());
3339 TNode<Object> arg_new_target = n.ArgumentOr(2, arg_target);
3340
3341 static_assert(n.ReceiverIndex() > n.TargetIndex());
3342 node->RemoveInput(n.ReceiverIndex());
3343 node->RemoveInput(n.TargetIndex());
3344
3345 // TODO(jgruber): This pattern essentially ensures that we have the correct
3346 // number of inputs for a given argument count. Wrap it in a helper function.
3347 static_assert(JSConstructNode::FirstArgumentIndex() == 2);
3348 while (arity < 3) {
3349 node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant());
3350 }
3351 while (arity-- > 3) {
3352 node->RemoveInput(arity);
3353 }
3354
3355 static_assert(JSConstructNode::TargetIndex() == 0);
3356 static_assert(JSConstructNode::NewTargetIndex() == 1);
3358 node->ReplaceInput(JSConstructNode::TargetIndex(), arg_target);
3359 node->ReplaceInput(JSConstructNode::NewTargetIndex(), arg_new_target);
3360 node->ReplaceInput(JSConstructNode::ArgumentIndex(0), arg_argument_list);
3361
3363 node, javascript()->ConstructWithArrayLike(p.frequency(), p.feedback()));
3365}
3366
3367// ES6 section 26.1.7 Reflect.getPrototypeOf ( target )
3369 JSCallNode n(node);
3370 Node* target = n.ArgumentOrUndefined(0, jsgraph());
3371 return ReduceObjectGetPrototype(node, target);
3372}
3373
3374// ES6 section #sec-object.create Object.create(proto, properties)
3376 JSCallNode n(node);
3377 Node* properties = n.ArgumentOrUndefined(1, jsgraph());
3378 if (properties != jsgraph()->UndefinedConstant()) return NoChange();
3379
3380 Node* context = n.context();
3381 FrameState frame_state = n.frame_state();
3382 Effect effect = n.effect();
3383 Control control = n.control();
3384 Node* prototype = n.ArgumentOrUndefined(0, jsgraph());
3385 node->ReplaceInput(0, prototype);
3386 node->ReplaceInput(1, context);
3387 node->ReplaceInput(2, frame_state);
3388 node->ReplaceInput(3, effect);
3389 node->ReplaceInput(4, control);
3390 node->TrimInputCount(5);
3391 NodeProperties::ChangeOp(node, javascript()->CreateObject());
3392 return Changed(node);
3393}
3394
3395// ES section #sec-reflect.get
3397 JSCallNode n(node);
3398 CallParameters const& p = n.Parameters();
3399 int arity = p.arity_without_implicit_args();
3400 if (arity != 2) return NoChange();
3401 Node* target = n.Argument(0);
3402 Node* key = n.Argument(1);
3403 Node* context = n.context();
3404 FrameState frame_state = n.frame_state();
3405 Effect effect = n.effect();
3406 Control control = n.control();
3407
3408 // Check whether {target} is a JSReceiver.
3409 Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target);
3410 Node* branch =
3411 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
3412
3413 // Throw an appropriate TypeError if the {target} is not a JSReceiver.
3414 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
3415 Node* efalse = effect;
3416 {
3417 if_false = efalse = graph()->NewNode(
3418 javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
3419 jsgraph()->ConstantNoHole(
3420 static_cast<int>(MessageTemplate::kCalledOnNonObject)),
3421 jsgraph()->HeapConstantNoHole(factory()->ReflectGet_string()), context,
3422 frame_state, efalse, if_false);
3423 }
3424
3425 // Otherwise just use the existing GetPropertyStub.
3426 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
3427 Node* etrue = effect;
3428 Node* vtrue;
3429 {
3430 Callable callable = Builtins::CallableFor(isolate(), Builtin::kGetProperty);
3431 auto call_descriptor = Linkage::GetStubCallDescriptor(
3432 graph()->zone(), callable.descriptor(),
3435 Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
3436 vtrue = etrue = if_true =
3437 graph()->NewNode(common()->Call(call_descriptor), stub_code, target,
3438 key, context, frame_state, etrue, if_true);
3439 }
3440
3441 // Rewire potential exception edges.
3442 Node* on_exception = nullptr;
3443 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
3444 // Create appropriate {IfException} and {IfSuccess} nodes.
3445 Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true);
3446 if_true = graph()->NewNode(common()->IfSuccess(), if_true);
3447 Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false);
3448 if_false = graph()->NewNode(common()->IfSuccess(), if_false);
3449
3450 // Join the exception edges.
3451 Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse);
3452 Node* ephi =
3453 graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge);
3454 Node* phi =
3456 extrue, exfalse, merge);
3457 ReplaceWithValue(on_exception, phi, ephi, merge);
3458 }
3459
3460 // Connect the throwing path to end.
3461 if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
3462 MergeControlToEnd(graph(), common(), if_false);
3463
3464 // Continue on the regular path.
3465 ReplaceWithValue(node, vtrue, etrue, if_true);
3466 return Changed(vtrue);
3467}
3468
3469// ES section #sec-reflect.has
3471 JSCallNode n(node);
3472 Node* target = n.ArgumentOrUndefined(0, jsgraph());
3473 Node* key = n.ArgumentOrUndefined(1, jsgraph());
3474 Node* context = n.context();
3475 Effect effect = n.effect();
3476 Control control = n.control();
3477 FrameState frame_state = n.frame_state();
3478
3479 // Check whether {target} is a JSReceiver.
3480 Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target);
3481 Node* branch =
3482 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
3483
3484 // Throw an appropriate TypeError if the {target} is not a JSReceiver.
3485 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
3486 Node* efalse = effect;
3487 {
3488 if_false = efalse = graph()->NewNode(
3489 javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
3490 jsgraph()->ConstantNoHole(
3491 static_cast<int>(MessageTemplate::kCalledOnNonObject)),
3492 jsgraph()->HeapConstantNoHole(factory()->ReflectHas_string()), context,
3493 frame_state, efalse, if_false);
3494 }
3495
3496 // Otherwise just use the existing {JSHasProperty} logic.
3497 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
3498 Node* etrue = effect;
3499 Node* vtrue;
3500 {
3501 // TODO(magardn): collect feedback so this can be optimized
3502 vtrue = etrue = if_true = graph()->NewNode(
3503 javascript()->HasProperty(FeedbackSource()), target, key,
3504 jsgraph()->UndefinedConstant(), context, frame_state, etrue, if_true);
3505 }
3506
3507 // Rewire potential exception edges.
3508 Node* on_exception = nullptr;
3509 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
3510 // Create appropriate {IfException} and {IfSuccess} nodes.
3511 Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true);
3512 if_true = graph()->NewNode(common()->IfSuccess(), if_true);
3513 Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false);
3514 if_false = graph()->NewNode(common()->IfSuccess(), if_false);
3515
3516 // Join the exception edges.
3517 Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse);
3518 Node* ephi =
3519 graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge);
3520 Node* phi =
3522 extrue, exfalse, merge);
3523 ReplaceWithValue(on_exception, phi, ephi, merge);
3524 }
3525
3526 // Connect the throwing path to end.
3527 if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
3528 MergeControlToEnd(graph(), common(), if_false);
3529
3530 // Continue on the regular path.
3531 ReplaceWithValue(node, vtrue, etrue, if_true);
3532 return Changed(vtrue);
3533}
3534
3535namespace {
3536
3537bool CanInlineArrayIteratingBuiltin(JSHeapBroker* broker,
3538 ZoneRefSet<Map> const& receiver_maps,
3539 ElementsKind* kind_return) {
3540 DCHECK_NE(0, receiver_maps.size());
3541 *kind_return = receiver_maps[0].elements_kind();
3542 for (MapRef map : receiver_maps) {
3543 if (!map.supports_fast_array_iteration(broker) ||
3544 !UnionElementsKindUptoSize(kind_return, map.elements_kind())) {
3545 return false;
3546 }
3547 }
3548 return true;
3549}
3550
3551bool CanInlineArrayResizingBuiltin(JSHeapBroker* broker,
3552 ZoneRefSet<Map> const& receiver_maps,
3553 std::vector<ElementsKind>* kinds,
3554 bool builtin_is_push = false) {
3555 DCHECK_NE(0, receiver_maps.size());
3556 for (MapRef map : receiver_maps) {
3557 if (!map.supports_fast_array_resize(broker)) return false;
3558 // TODO(turbofan): We should also handle fast holey double elements once
3559 // we got the hole NaN mess sorted out in TurboFan/V8.
3560 if (map.elements_kind() == HOLEY_DOUBLE_ELEMENTS && !builtin_is_push) {
3561 return false;
3562 }
3563 ElementsKind current_kind = map.elements_kind();
3564 auto kind_ptr = kinds->data();
3565 size_t i;
3566 for (i = 0; i < kinds->size(); i++, kind_ptr++) {
3567 if (UnionElementsKindUptoPackedness(kind_ptr, current_kind)) {
3568 break;
3569 }
3570 }
3571 if (i == kinds->size()) kinds->push_back(current_kind);
3572 }
3573 return true;
3574}
3575
3576// Wraps common setup code for iterating array builtins.
3577class IteratingArrayBuiltinHelper {
3578 public:
3579 IteratingArrayBuiltinHelper(Node* node, JSHeapBroker* broker,
3580 JSGraph* jsgraph,
3581 CompilationDependencies* dependencies)
3582 : receiver_(NodeProperties::GetValueInput(node, 1)),
3583 effect_(NodeProperties::GetEffectInput(node)),
3584 control_(NodeProperties::GetControlInput(node)),
3586 if (!v8_flags.turbo_inline_array_builtins) return;
3587
3588 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
3589 const CallParameters& p = CallParametersOf(node->op());
3590 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
3591 return;
3592 }
3593
3594 // Try to determine the {receiver} map.
3595 if (!inference_.HaveMaps()) return;
3596 ZoneRefSet<Map> const& receiver_maps = inference_.GetMaps();
3597
3598 if (!CanInlineArrayIteratingBuiltin(broker, receiver_maps,
3599 &elements_kind_)) {
3600 return;
3601 }
3602
3603 // TODO(jgruber): May only be needed for holey elements kinds.
3604 if (!dependencies->DependOnNoElementsProtector()) return;
3605
3606 has_stability_dependency_ = inference_.RelyOnMapsPreferStability(
3607 dependencies, jsgraph, &effect_, control_, p.feedback());
3608
3609 can_reduce_ = true;
3610 }
3611
3612 bool can_reduce() const { return can_reduce_; }
3613 bool has_stability_dependency() const { return has_stability_dependency_; }
3614 Effect effect() const { return effect_; }
3615 Control control() const { return control_; }
3616 MapInference* inference() { return &inference_; }
3617 ElementsKind elements_kind() const { return elements_kind_; }
3618
3619 private:
3620 bool can_reduce_ = false;
3623 Effect effect_;
3624 Control control_;
3625 MapInference inference_;
3627};
3628
3629} // namespace
3630
3632 SharedFunctionInfoRef shared) {
3633 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3634 if (!h.can_reduce()) return h.inference()->NoChange();
3635
3637 a.InitializeEffectControl(h.effect(), h.control());
3638 TNode<Object> subgraph = a.ReduceArrayPrototypeForEach(
3639 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared);
3640 return ReplaceWithSubgraph(&a, subgraph);
3641}
3642
3644 SharedFunctionInfoRef shared) {
3645 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3646 if (!h.can_reduce()) return h.inference()->NoChange();
3647
3649 a.InitializeEffectControl(h.effect(), h.control());
3650 TNode<Object> subgraph = a.ReduceArrayPrototypeReduce(
3651 h.inference(), h.has_stability_dependency(), h.elements_kind(),
3653 return ReplaceWithSubgraph(&a, subgraph);
3654}
3655
3657 SharedFunctionInfoRef shared) {
3658 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3659 if (!h.can_reduce()) return h.inference()->NoChange();
3660
3662 a.InitializeEffectControl(h.effect(), h.control());
3663 TNode<Object> subgraph = a.ReduceArrayPrototypeReduce(
3664 h.inference(), h.has_stability_dependency(), h.elements_kind(),
3666 return ReplaceWithSubgraph(&a, subgraph);
3667}
3668
3670 SharedFunctionInfoRef shared) {
3671 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3672 if (!h.can_reduce()) return h.inference()->NoChange();
3673
3674 // Calls CreateArray and thus requires this additional protector dependency.
3675 if (!dependencies()->DependOnArraySpeciesProtector()) {
3676 return h.inference()->NoChange();
3677 }
3678
3680 a.InitializeEffectControl(h.effect(), h.control());
3681
3682 TNode<Object> subgraph =
3683 a.ReduceArrayPrototypeMap(h.inference(), h.has_stability_dependency(),
3684 h.elements_kind(), shared, native_context());
3685 return ReplaceWithSubgraph(&a, subgraph);
3686}
3687
3689 SharedFunctionInfoRef shared) {
3690 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3691 if (!h.can_reduce()) return h.inference()->NoChange();
3692
3693 // Calls CreateArray and thus requires this additional protector dependency.
3694 if (!dependencies()->DependOnArraySpeciesProtector()) {
3695 return h.inference()->NoChange();
3696 }
3697
3699 a.InitializeEffectControl(h.effect(), h.control());
3700
3701 TNode<Object> subgraph =
3702 a.ReduceArrayPrototypeFilter(h.inference(), h.has_stability_dependency(),
3703 h.elements_kind(), shared, native_context());
3704 return ReplaceWithSubgraph(&a, subgraph);
3705}
3706
3708 SharedFunctionInfoRef shared) {
3709 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3710 if (!h.can_reduce()) return h.inference()->NoChange();
3711
3713 a.InitializeEffectControl(h.effect(), h.control());
3714
3715 TNode<Object> subgraph = a.ReduceArrayPrototypeFind(
3716 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared,
3718 return ReplaceWithSubgraph(&a, subgraph);
3719}
3720
3722 SharedFunctionInfoRef shared) {
3723 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3724 if (!h.can_reduce()) return h.inference()->NoChange();
3725
3727 a.InitializeEffectControl(h.effect(), h.control());
3728
3729 TNode<Object> subgraph = a.ReduceArrayPrototypeFind(
3730 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared,
3732 return ReplaceWithSubgraph(&a, subgraph);
3733}
3734
3736 SharedFunctionInfoRef shared) {
3737 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3738 if (!h.can_reduce()) return h.inference()->NoChange();
3739
3741 a.InitializeEffectControl(h.effect(), h.control());
3742
3743 TNode<Object> subgraph = a.ReduceArrayPrototypeEverySome(
3744 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared,
3746 return ReplaceWithSubgraph(&a, subgraph);
3747}
3748
3749// ES7 Array.prototype.inludes(searchElement[, fromIndex])
3750// #sec-array.prototype.includes
3752 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3753 if (!h.can_reduce()) return h.inference()->NoChange();
3754
3756 a.InitializeEffectControl(h.effect(), h.control());
3757
3758 TNode<Object> subgraph = a.ReduceArrayPrototypeIndexOfIncludes(
3759 h.elements_kind(), ArrayIndexOfIncludesVariant::kIncludes);
3760 return ReplaceWithSubgraph(&a, subgraph);
3761}
3762
3763// ES6 Array.prototype.indexOf(searchElement[, fromIndex])
3764// #sec-array.prototype.indexof
3766 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3767 if (!h.can_reduce()) return h.inference()->NoChange();
3768
3770 a.InitializeEffectControl(h.effect(), h.control());
3771
3772 TNode<Object> subgraph = a.ReduceArrayPrototypeIndexOfIncludes(
3773 h.elements_kind(), ArrayIndexOfIncludesVariant::kIndexOf);
3774 return ReplaceWithSubgraph(&a, subgraph);
3775}
3776
3778 SharedFunctionInfoRef shared) {
3779 IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
3780 if (!h.can_reduce()) return h.inference()->NoChange();
3781
3783 a.InitializeEffectControl(h.effect(), h.control());
3784
3785 TNode<Object> subgraph = a.ReduceArrayPrototypeEverySome(
3786 h.inference(), h.has_stability_dependency(), h.elements_kind(), shared,
3788 return ReplaceWithSubgraph(&a, subgraph);
3789}
3790
3791#if V8_ENABLE_WEBASSEMBLY
3792
3793namespace {
3794
3795bool CanInlineJSToWasmCall(const wasm::CanonicalSig* wasm_signature) {
3796 if (wasm_signature->return_count() > 1) {
3797 return false;
3798 }
3799
3800 for (auto type : wasm_signature->all()) {
3801#if defined(V8_TARGET_ARCH_32_BIT)
3802 if (type == wasm::kWasmI64) return false;
3803#endif
3804 if (type != wasm::kWasmI32 && type != wasm::kWasmI64 &&
3805 type != wasm::kWasmF32 && type != wasm::kWasmF64 &&
3806 type != wasm::kWasmExternRef) {
3807 return false;
3808 }
3809 }
3810
3811 return true;
3812}
3813
3814} // namespace
3815
3816Reduction JSCallReducer::ReduceCallWasmFunction(Node* node,
3817 SharedFunctionInfoRef shared) {
3819
3820 JSCallNode n(node);
3821 const CallParameters& p = n.Parameters();
3822
3823 // Avoid deoptimization loops
3824 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
3825 return NoChange();
3826 }
3827
3828 // Read the trusted object only once to ensure a consistent view on it.
3829 Tagged<Object> trusted_data = shared.object()->GetTrustedData();
3830 if (!IsWasmExportedFunctionData(trusted_data)) return NoChange();
3831 Tagged<WasmExportedFunctionData> function_data =
3833
3834 if (function_data->is_promising()) return NoChange();
3835
3837 function_data->instance_data();
3838 const wasm::CanonicalSig* wasm_signature = function_data->sig();
3839 if (!CanInlineJSToWasmCall(wasm_signature)) {
3840 return NoChange();
3841 }
3842
3843 wasm::NativeModule* native_module = instance_data->native_module();
3844 const wasm::WasmModule* wasm_module = native_module->module();
3845 int wasm_function_index = function_data->function_index();
3846
3847 if (wasm_module_for_inlining_ == nullptr) {
3848 wasm_module_for_inlining_ = wasm_module;
3849 }
3850
3851 // TODO(mliedtke): We should be able to remove module, signature, native
3852 // module and function index from the SharedFunctionInfoRef. However, for some
3853 // reason I may dereference the SharedFunctionInfoRef here but not in
3854 // JSInliningHeuristic later on.
3855 const Operator* op =
3856 javascript()->CallWasm(wasm_module, wasm_signature, wasm_function_index,
3857 shared, native_module, p.feedback());
3858
3859 // Remove additional inputs
3860 size_t actual_arity = n.ArgumentCount();
3862 DCHECK_EQ(actual_arity + JSWasmCallNode::kExtraInputCount - 1,
3863 n.FeedbackVectorIndex());
3864 size_t expected_arity = wasm_signature->parameter_count();
3865
3866 while (actual_arity > expected_arity) {
3867 int removal_index =
3868 static_cast<int>(n.FirstArgumentIndex() + expected_arity);
3869 DCHECK_LT(removal_index, static_cast<int>(node->InputCount()));
3870 node->RemoveInput(removal_index);
3871 actual_arity--;
3872 }
3873
3874 // Add missing inputs
3875 while (actual_arity < expected_arity) {
3876 int insertion_index = n.ArgumentIndex(n.ArgumentCount());
3877 node->InsertInput(graph()->zone(), insertion_index,
3879 actual_arity++;
3880 }
3881
3882 NodeProperties::ChangeOp(node, op);
3883 return Changed(node);
3884}
3885#endif // V8_ENABLE_WEBASSEMBLY
3886
3888 SharedFunctionInfoRef shared) {
3889 JSCallNode n(node);
3890 CallParameters const& p = n.Parameters();
3891 int const argc = p.arity_without_implicit_args();
3892 Node* target = n.target();
3893 Node* global_proxy = jsgraph()->ConstantNoHole(
3894 native_context().global_proxy_object(broker()), broker());
3896 ? global_proxy
3897 : n.receiver();
3898 Node* context = n.context();
3899 Effect effect = n.effect();
3900 Control control = n.control();
3901 FrameState frame_state = n.frame_state();
3902
3903 if (!shared.function_template_info(broker()).has_value()) {
3905 broker(), "FunctionTemplateInfo for function with SFI " << shared);
3906 return NoChange();
3907 }
3908
3909 // See if we can optimize this API call to {shared}.
3910 FunctionTemplateInfoRef function_template_info(
3911 shared.function_template_info(broker()).value());
3912
3913 if (function_template_info.accept_any_receiver() &&
3914 function_template_info.is_signature_undefined(broker())) {
3915 // We might be able to
3916 // optimize the API call depending on the {function_template_info}.
3917 // If the API function accepts any kind of {receiver}, we only need to
3918 // ensure that the {receiver} is actually a JSReceiver at this point,
3919 // and also pass that as the {holder}. There are two independent bits
3920 // here:
3921 //
3922 // a. When the "accept any receiver" bit is set, it means we don't
3923 // need to perform access checks, even if the {receiver}'s map
3924 // has the "needs access check" bit set.
3925 // b. When the {function_template_info} has no signature, we don't
3926 // need to do the compatible receiver check, since all receivers
3927 // are considered compatible at that point, and the {receiver}
3928 // will be pass as the {holder}.
3929 //
3930 receiver = effect = graph()->NewNode(
3931 simplified()->ConvertReceiver(p.convert_mode()), receiver,
3932 jsgraph()->ConstantNoHole(native_context(), broker()), global_proxy,
3933 effect, control);
3934 } else {
3935 // Try to infer the {receiver} maps from the graph.
3936 MapInference inference(broker(), receiver, effect);
3937 if (inference.HaveMaps()) {
3938 ZoneRefSet<Map> const& receiver_maps = inference.GetMaps();
3939 MapRef first_receiver_map = receiver_maps[0];
3940
3941 // See if we can constant-fold the compatible receiver checks.
3942 HolderLookupResult api_holder =
3943 function_template_info.LookupHolderOfExpectedType(broker(),
3944 first_receiver_map);
3945 if (api_holder.lookup == CallOptimization::kHolderNotFound) {
3946 return inference.NoChange();
3947 }
3948
3949 // Check that all {receiver_maps} are actually JSReceiver maps and
3950 // that the {function_template_info} accepts them without access
3951 // checks (even if "access check needed" is set for {receiver}).
3952 //
3953 // Note that we don't need to know the concrete {receiver} maps here,
3954 // meaning it's fine if the {receiver_maps} are unreliable, and we also
3955 // don't need to install any stability dependencies, since the only
3956 // relevant information regarding the {receiver} is the Map::constructor
3957 // field on the root map (which is different from the JavaScript exposed
3958 // "constructor" property) and that field cannot change.
3959 //
3960 // So if we know that {receiver} had a certain constructor at some point
3961 // in the past (i.e. it had a certain map), then this constructor is going
3962 // to be the same later, since this information cannot change with map
3963 // transitions.
3964 //
3965 // The same is true for the instance type, e.g. we still know that the
3966 // instance type is JSObject even if that information is unreliable, and
3967 // the "access check needed" bit, which also cannot change later.
3968 CHECK(first_receiver_map.IsJSReceiverMap());
3969 CHECK(!first_receiver_map.is_access_check_needed() ||
3970 function_template_info.accept_any_receiver());
3971
3972 for (size_t i = 1; i < receiver_maps.size(); ++i) {
3973 MapRef receiver_map = receiver_maps[i];
3974 HolderLookupResult holder_i =
3975 function_template_info.LookupHolderOfExpectedType(broker(),
3976 receiver_map);
3977
3978 if (api_holder.lookup != holder_i.lookup) return inference.NoChange();
3981 if (holder_i.lookup == CallOptimization::kHolderFound) {
3982 DCHECK(api_holder.holder.has_value() && holder_i.holder.has_value());
3983 if (!api_holder.holder->equals(*holder_i.holder)) {
3984 return inference.NoChange();
3985 }
3986 }
3987
3988 CHECK(receiver_map.IsJSReceiverMap());
3989 CHECK(!receiver_map.is_access_check_needed() ||
3990 function_template_info.accept_any_receiver());
3991 }
3992
3994 !inference.RelyOnMapsViaStability(dependencies())) {
3995 // We were not able to make the receiver maps reliable without map
3996 // checks but doing map checks would lead to deopt loops, so give up.
3997 return inference.NoChange();
3998 }
3999
4000 // TODO(neis): The maps were used in a way that does not actually require
4001 // map checks or stability dependencies.
4002 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
4003 control, p.feedback());
4004 } else {
4005 // We don't have enough information to eliminate the access check
4006 // and/or the compatible receiver check, so use the generic builtin
4007 // that does those checks dynamically. This is still significantly
4008 // faster than the generic call sequence.
4009 Builtin builtin_name;
4010 if (function_template_info.accept_any_receiver()) {
4011 DCHECK(!function_template_info.is_signature_undefined(broker()));
4012 builtin_name = Builtin::kCallFunctionTemplate_CheckCompatibleReceiver;
4013 } else if (function_template_info.is_signature_undefined(broker())) {
4014 builtin_name = Builtin::kCallFunctionTemplate_CheckAccess;
4015 } else {
4016 builtin_name =
4017 Builtin::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver;
4018 }
4019
4020 // The CallFunctionTemplate builtin requires the {receiver} to be
4021 // an actual JSReceiver, so make sure we do the proper conversion
4022 // first if necessary.
4023 receiver = effect = graph()->NewNode(
4024 simplified()->ConvertReceiver(p.convert_mode()), receiver,
4025 jsgraph()->ConstantNoHole(native_context(), broker()), global_proxy,
4026 effect, control);
4027
4028 Callable callable = Builtins::CallableFor(isolate(), builtin_name);
4029 auto call_descriptor = Linkage::GetStubCallDescriptor(
4030 graph()->zone(), callable.descriptor(),
4031 argc + 1 /* implicit receiver */, CallDescriptor::kNeedsFrameState);
4032 node->RemoveInput(n.FeedbackVectorIndex());
4033 node->InsertInput(graph()->zone(), 0,
4034 jsgraph()->HeapConstantNoHole(callable.code()));
4035 node->ReplaceInput(
4036 1, jsgraph()->ConstantNoHole(function_template_info, broker()));
4037 node->InsertInput(graph()->zone(), 2,
4038 jsgraph()->Int32Constant(JSParameterCount(argc)));
4039 node->ReplaceInput(3, receiver); // Update receiver input.
4040 node->ReplaceInput(6 + argc, effect); // Update effect input.
4041 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
4042 return Changed(node);
4043 }
4044 }
4045
4046 // TODO(turbofan): Consider introducing a JSCallApiCallback operator for
4047 // this and lower it during JSGenericLowering, and unify this with the
4048 // JSNativeContextSpecialization::InlineApiCall method a bit.
4049 compiler::OptionalObjectRef maybe_callback_data =
4050 function_template_info.callback_data(broker());
4051 // Check if the function has an associated C++ code to execute.
4052 if (!maybe_callback_data.has_value()) {
4053 // TODO(ishell): consider generating "return undefined" for empty function
4054 // instead of failing.
4055 TRACE_BROKER_MISSING(broker(), "call code for function template info "
4056 << function_template_info);
4057 return NoChange();
4058 }
4059
4060 // Handles overloaded functions.
4062 broker(), function_template_info, argc);
4063
4064 if (c_function.address) {
4065 FastApiCallReducerAssembler a(this, node, function_template_info,
4066 c_function, receiver, shared, target, argc,
4067 effect);
4068 Node* fast_call_subgraph = a.ReduceFastApiCall();
4069
4070 return Replace(fast_call_subgraph);
4071 }
4072
4073 // Slow call
4074
4075 bool no_profiling = broker()->dependencies()->DependOnNoProfilingProtector();
4076 Callable call_api_callback = Builtins::CallableFor(
4077 isolate(), no_profiling ? Builtin::kCallApiCallbackOptimizedNoProfiling
4078 : Builtin::kCallApiCallbackOptimized);
4079 CallInterfaceDescriptor cid = call_api_callback.descriptor();
4080 auto call_descriptor =
4081 Linkage::GetStubCallDescriptor(graph()->zone(), cid, argc + 1 /*
4082 implicit receiver */, CallDescriptor::kNeedsFrameState);
4083 ApiFunction api_function(function_template_info.callback(broker()));
4084 ExternalReference function_reference = ExternalReference::Create(
4085 &api_function, ExternalReference::DIRECT_API_CALL);
4086
4087 Node* continuation_frame_state = CreateInlinedApiFunctionFrameState(
4088 jsgraph(), shared, target, context, receiver, frame_state);
4089
4090 node->RemoveInput(n.FeedbackVectorIndex());
4091 node->InsertInput(graph()->zone(), 0,
4092 jsgraph()->HeapConstantNoHole(call_api_callback.code()));
4093 node->ReplaceInput(1, jsgraph()->ExternalConstant(function_reference));
4094 node->InsertInput(graph()->zone(), 2, jsgraph()->ConstantNoHole(argc));
4095 node->InsertInput(
4096 graph()->zone(), 3,
4097 jsgraph()->HeapConstantNoHole(function_template_info.object()));
4098 node->ReplaceInput(4, receiver); // Update receiver input.
4099 // 6 + argc is context input.
4100 node->ReplaceInput(5 + argc + 1, continuation_frame_state);
4101 node->ReplaceInput(5 + argc + 2, effect);
4102 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
4103 return Changed(node);
4104}
4105
4106namespace {
4107
4108// Check whether elements aren't mutated; we play it extremely safe here by
4109// explicitly checking that {node} is only used by {LoadField} or
4110// {LoadElement}.
4111bool IsSafeArgumentsElements(Node* node) {
4112 for (Edge const edge : node->use_edges()) {
4113 if (!NodeProperties::IsValueEdge(edge)) continue;
4114 if (edge.from()->opcode() != IrOpcode::kLoadField &&
4115 edge.from()->opcode() != IrOpcode::kLoadElement) {
4116 return false;
4117 }
4118 }
4119 return true;
4120}
4121
4122#ifdef DEBUG
4123bool IsCallOrConstructWithArrayLike(Node* node) {
4124 return node->opcode() == IrOpcode::kJSCallWithArrayLike ||
4125 node->opcode() == IrOpcode::kJSConstructWithArrayLike;
4126}
4127#endif
4128
4129bool IsCallOrConstructWithSpread(Node* node) {
4130 return node->opcode() == IrOpcode::kJSCallWithSpread ||
4131 node->opcode() == IrOpcode::kJSConstructWithSpread;
4132}
4133
4134bool IsCallWithArrayLikeOrSpread(Node* node) {
4135 return node->opcode() == IrOpcode::kJSCallWithArrayLike ||
4136 node->opcode() == IrOpcode::kJSCallWithSpread;
4137}
4138
4139} // namespace
4140
4143 if (kind == HOLEY_DOUBLE_ELEMENTS) {
4144 return graph()->NewNode(simplified()->ChangeFloat64HoleToTagged(), value);
4145 }
4146 return graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
4147}
4148
4150 JSConstructNode n(construct);
4151 Node* new_target = n.new_target();
4152 Control control = n.control();
4153
4154 Node* check =
4155 graph()->NewNode(simplified()->ObjectIsConstructor(), new_target);
4156 Node* check_branch =
4157 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
4158 Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
4159 Node* check_throw = check_fail = graph()->NewNode(
4160 javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
4161 jsgraph()->ConstantNoHole(
4162 static_cast<int>(MessageTemplate::kNotConstructor)),
4163 new_target, n.context(), n.frame_state(), n.effect(), check_fail);
4164 control = graph()->NewNode(common()->IfTrue(), check_branch);
4165 NodeProperties::ReplaceControlInput(construct, control);
4166
4167 // Rewire potential exception edges.
4168 Node* on_exception = nullptr;
4169 if (NodeProperties::IsExceptionalCall(construct, &on_exception)) {
4170 // Create appropriate {IfException} and {IfSuccess} nodes.
4171 Node* if_exception =
4172 graph()->NewNode(common()->IfException(), check_throw, check_fail);
4173 check_fail = graph()->NewNode(common()->IfSuccess(), check_fail);
4174
4175 // Join the exception edges.
4176 Node* merge =
4177 graph()->NewNode(common()->Merge(2), if_exception, on_exception);
4178 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception,
4179 on_exception, merge);
4180 Node* phi =
4182 if_exception, on_exception, merge);
4183 ReplaceWithValue(on_exception, phi, ephi, merge);
4184 merge->ReplaceInput(1, on_exception);
4185 ephi->ReplaceInput(1, on_exception);
4186 phi->ReplaceInput(1, on_exception);
4187 }
4188
4189 // The above %ThrowTypeError runtime call is an unconditional throw,
4190 // making it impossible to return a successful completion in this case. We
4191 // simply connect the successful completion to the graph end.
4192 Node* throw_node =
4193 graph()->NewNode(common()->Throw(), check_throw, check_fail);
4194 MergeControlToEnd(graph(), common(), throw_node);
4195}
4196
4197namespace {
4198
4199bool ShouldUseCallICFeedback(Node* node) {
4200 HeapObjectMatcher m(node);
4201 if (m.HasResolvedValue() || m.IsCheckClosure() || m.IsJSCreateClosure()) {
4202 // Don't use CallIC feedback when we know the function
4203 // being called, i.e. either know the closure itself or
4204 // at least the SharedFunctionInfo.
4205 return false;
4206 } else if (m.IsPhi()) {
4207 // Protect against endless loops here.
4208 Node* control = NodeProperties::GetControlInput(node);
4209 if (control->opcode() == IrOpcode::kLoop ||
4210 control->opcode() == IrOpcode::kDead)
4211 return false;
4212 // Check if {node} is a Phi of nodes which shouldn't
4213 // use CallIC feedback (not looking through loops).
4214 int const value_input_count = m.node()->op()->ValueInputCount();
4215 for (int n = 0; n < value_input_count; ++n) {
4216 if (ShouldUseCallICFeedback(node->InputAt(n))) return true;
4217 }
4218 return false;
4219 }
4220 return true;
4221}
4222
4223} // namespace
4224
4226 uint32_t array_length,
4227 const FeedbackSource& feedback_source,
4228 Effect effect, Control control) {
4229 Node* length = effect = graph()->NewNode(
4230 simplified()->LoadField(AccessBuilder::ForJSArrayLength(elements_kind)),
4231 array, effect, control);
4232 Node* check = graph()->NewNode(simplified()->NumberEqual(), length,
4233 jsgraph()->ConstantNoHole(array_length));
4234 return graph()->NewNode(
4235 simplified()->CheckIf(DeoptimizeReason::kArrayLengthChanged,
4236 feedback_source),
4237 check, effect, control);
4238}
4239
4242 Node* node, Node* arguments_list, int arraylike_or_spread_index,
4243 CallFrequency const& frequency, FeedbackSource const& feedback,
4244 SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) {
4245 DCHECK_EQ(arguments_list->opcode(), IrOpcode::kJSCreateArguments);
4246
4247 // Check if {node} is the only value user of {arguments_list} (except for
4248 // value uses in frame states). If not, we give up for now.
4249 for (Edge edge : arguments_list->use_edges()) {
4250 if (!NodeProperties::IsValueEdge(edge)) continue;
4251 Node* const user = edge.from();
4252 switch (user->opcode()) {
4253 case IrOpcode::kCheckMaps:
4254 case IrOpcode::kFrameState:
4255 case IrOpcode::kStateValues:
4256 case IrOpcode::kReferenceEqual:
4257 case IrOpcode::kReturn:
4258 // Ignore safe uses that definitely don't mess with the arguments.
4259 continue;
4260 case IrOpcode::kLoadField: {
4261 DCHECK_EQ(arguments_list, user->InputAt(0));
4262 FieldAccess const& access = FieldAccessOf(user->op());
4263 if (access.offset == JSArray::kLengthOffset) {
4264 // Ignore uses for arguments#length.
4265 static_assert(
4266 static_cast<int>(JSArray::kLengthOffset) ==
4267 static_cast<int>(JSStrictArgumentsObject::kLengthOffset));
4268 static_assert(
4269 static_cast<int>(JSArray::kLengthOffset) ==
4270 static_cast<int>(JSSloppyArgumentsObject::kLengthOffset));
4271 continue;
4272 } else if (access.offset == JSObject::kElementsOffset) {
4273 // Ignore safe uses for arguments#elements.
4274 if (IsSafeArgumentsElements(user)) continue;
4275 }
4276 break;
4277 }
4278 case IrOpcode::kJSCallWithArrayLike: {
4279 // Ignore uses as argumentsList input to calls with array like.
4281 if (edge.index() == n.ArgumentIndex(0)) continue;
4282 break;
4283 }
4284 case IrOpcode::kJSConstructWithArrayLike: {
4285 // Ignore uses as argumentsList input to calls with array like.
4287 if (edge.index() == n.ArgumentIndex(0)) continue;
4288 break;
4289 }
4290 case IrOpcode::kJSCallWithSpread: {
4291 // Ignore uses as spread input to calls with spread.
4292 JSCallWithSpreadNode n(user);
4293 if (edge.index() == n.LastArgumentIndex()) continue;
4294 break;
4295 }
4296 case IrOpcode::kJSConstructWithSpread: {
4297 // Ignore uses as spread input to construct with spread.
4299 if (edge.index() == n.LastArgumentIndex()) continue;
4300 break;
4301 }
4302 default:
4303 break;
4304 }
4305 // We cannot currently reduce the {node} to something better than what
4306 // it already is, but we might be able to do something about the {node}
4307 // later, so put it on the waitlist and try again during finalization.
4308 waitlist_.insert(node);
4309 return NoChange();
4310 }
4311
4312 // Get to the actual frame state from which to extract the arguments;
4313 // we can only optimize this in case the {node} was already inlined into
4314 // some other function (and same for the {arguments_list}).
4315 CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op());
4316 FrameState frame_state =
4318
4319 int formal_parameter_count;
4320 {
4322 if (!frame_state.frame_state_info().shared_info().ToHandle(&shared)) {
4323 return NoChange();
4324 }
4325 formal_parameter_count =
4326 MakeRef(broker(), shared)
4327 .internal_formal_parameter_count_without_receiver();
4328 }
4329
4331 // Mapped arguments (sloppy mode) that are aliased can only be handled
4332 // here if there's no side-effect between the {node} and the {arg_array}.
4333 // TODO(turbofan): Further relax this constraint.
4334 if (formal_parameter_count != 0) {
4335 Node* effect = NodeProperties::GetEffectInput(node);
4337 arguments_list)) {
4338 return NoChange();
4339 }
4340 }
4341 }
4342
4343 // For call/construct with spread, we need to also install a code
4344 // dependency on the array iterator lookup protector cell to ensure
4345 // that no one messed with the %ArrayIteratorPrototype%.next method.
4346 if (IsCallOrConstructWithSpread(node)) {
4347 if (!dependencies()->DependOnArrayIteratorProtector()) return NoChange();
4348 }
4349
4350 // Remove the {arguments_list} input from the {node}.
4351 node->RemoveInput(arraylike_or_spread_index);
4352
4353 // The index of the first relevant parameter. Only non-zero when looking at
4354 // rest parameters, in which case it is set to the index of the first rest
4355 // parameter.
4356 const int start_index = (type == CreateArgumentsType::kRestParameter)
4357 ? formal_parameter_count
4358 : 0;
4359
4360 // After removing the arraylike or spread object, the argument count is:
4361 int argc =
4362 arraylike_or_spread_index - JSCallOrConstructNode::FirstArgumentIndex();
4363 // Check if are spreading to inlined arguments or to the arguments of
4364 // the outermost function.
4365 if (frame_state.outer_frame_state()->opcode() != IrOpcode::kFrameState) {
4366 Operator const* op;
4367 if (IsCallWithArrayLikeOrSpread(node)) {
4368 static constexpr int kTargetAndReceiver = 2;
4369 op = javascript()->CallForwardVarargs(argc + kTargetAndReceiver,
4370 start_index);
4371 } else {
4372 static constexpr int kTargetAndNewTarget = 2;
4373 op = javascript()->ConstructForwardVarargs(argc + kTargetAndNewTarget,
4374 start_index);
4375 }
4377 NodeProperties::ChangeOp(node, op);
4378 return Changed(node);
4379 }
4380 // Get to the actual frame state from which to extract the arguments;
4381 // we can only optimize this in case the {node} was already inlined into
4382 // some other function (and same for the {arg_array}).
4383 FrameState outer_state{frame_state.outer_frame_state()};
4384 FrameStateInfo outer_info = outer_state.frame_state_info();
4385 if (outer_info.type() == FrameStateType::kInlinedExtraArguments) {
4386 // Need to take the parameters from the inlined extra arguments frame state.
4387 frame_state = outer_state;
4388 }
4389 // Add the actual parameters to the {node}, skipping the receiver.
4390 StateValuesAccess parameters_access(frame_state.parameters());
4391 for (auto it = parameters_access.begin_without_receiver_and_skip(start_index);
4392 !it.done(); ++it) {
4393 DCHECK_NOT_NULL(it.node());
4394 node->InsertInput(graph()->zone(),
4395 JSCallOrConstructNode::ArgumentIndex(argc++), it.node());
4396 }
4397
4398 if (IsCallWithArrayLikeOrSpread(node)) {
4400 node, javascript()->Call(JSCallNode::ArityForArgc(argc), frequency,
4401 feedback, ConvertReceiverMode::kAny,
4402 speculation_mode, feedback_relation));
4403 return Changed(node).FollowedBy(ReduceJSCall(node));
4404 } else {
4406 node, javascript()->Construct(JSConstructNode::ArityForArgc(argc),
4407 frequency, feedback));
4408
4409 // Check whether the given new target value is a constructor function. The
4410 // replacement {JSConstruct} operator only checks the passed target value
4411 // but relies on the new target value to be implicitly valid.
4412 CheckIfConstructor(node);
4413 return Changed(node).FollowedBy(ReduceJSConstruct(node));
4414 }
4415}
4416
4418 Node* node, int argument_count, int arraylike_or_spread_index,
4419 CallFrequency const& frequency, FeedbackSource const& feedback_source,
4420 SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation,
4421 Node* target, Effect effect, Control control) {
4422 DCHECK(IsCallOrConstructWithArrayLike(node) ||
4423 IsCallOrConstructWithSpread(node));
4425 feedback_source.IsValid());
4426
4427 Node* arguments_list =
4428 NodeProperties::GetValueInput(node, arraylike_or_spread_index);
4429
4430 if (arguments_list->opcode() == IrOpcode::kJSCreateArguments) {
4432 node, arguments_list, arraylike_or_spread_index, frequency,
4433 feedback_source, speculation_mode, feedback_relation);
4434 }
4435
4436 if (!v8_flags.turbo_optimize_apply) return NoChange();
4437
4438 // Optimization of construct nodes not supported yet.
4439 if (!IsCallWithArrayLikeOrSpread(node)) return NoChange();
4440
4441 // Avoid deoptimization loops.
4442 if (speculation_mode != SpeculationMode::kAllowSpeculation) return NoChange();
4443
4444 // Only optimize with array literals.
4445 if (arguments_list->opcode() != IrOpcode::kJSCreateLiteralArray &&
4446 arguments_list->opcode() != IrOpcode::kJSCreateEmptyLiteralArray) {
4447 return NoChange();
4448 }
4449
4450 // For call/construct with spread, we need to also install a code
4451 // dependency on the array iterator lookup protector cell to ensure
4452 // that no one messed with the %ArrayIteratorPrototype%.next method.
4453 if (IsCallOrConstructWithSpread(node)) {
4454 if (!dependencies()->DependOnArrayIteratorProtector()) return NoChange();
4455 }
4456
4457 if (arguments_list->opcode() == IrOpcode::kJSCreateEmptyLiteralArray) {
4459 return NoChange(); // Avoid infinite recursion.
4460 }
4461 JSCallReducerAssembler a(this, node);
4462 Node* subgraph = a.ReduceJSCallWithArrayLikeOrSpreadOfEmpty(
4464 return ReplaceWithSubgraph(&a, subgraph);
4465 }
4466
4467 DCHECK_EQ(arguments_list->opcode(), IrOpcode::kJSCreateLiteralArray);
4468 int new_argument_count;
4469
4470 // Find array length and elements' kind from the feedback's allocation
4471 // site's boilerplate JSArray.
4472 JSCreateLiteralOpNode args_node(arguments_list);
4473 CreateLiteralParameters const& args_params = args_node.Parameters();
4474 const FeedbackSource& array_feedback = args_params.feedback();
4475 const ProcessedFeedback& feedback =
4476 broker()->GetFeedbackForArrayOrObjectLiteral(array_feedback);
4477 if (feedback.IsInsufficient()) return NoChange();
4478
4479 AllocationSiteRef site = feedback.AsLiteral().value();
4480 if (!site.boilerplate(broker()).has_value()) return NoChange();
4481
4482 JSArrayRef boilerplate_array = site.boilerplate(broker())->AsJSArray();
4483 int const array_length =
4484 boilerplate_array.GetBoilerplateLength(broker()).AsSmi();
4485
4486 // We'll replace the arguments_list input with {array_length} element loads.
4487 new_argument_count = argument_count - 1 + array_length;
4488
4489 // Do not optimize calls with a large number of arguments.
4490 // Arbitrarily sets the limit to 32 arguments.
4491 const int kMaxArityForOptimizedFunctionApply = 32;
4492 if (new_argument_count > kMaxArityForOptimizedFunctionApply) {
4493 return NoChange();
4494 }
4495
4496 // Determine the array's map.
4497 MapRef array_map = boilerplate_array.map(broker());
4498 if (!array_map.supports_fast_array_iteration(broker())) {
4499 return NoChange();
4500 }
4501
4502 // Check and depend on NoElementsProtector.
4503 if (!dependencies()->DependOnNoElementsProtector()) {
4504 return NoChange();
4505 }
4506
4507 // Remove the {arguments_list} node which will be replaced by a sequence of
4508 // LoadElement nodes.
4509 node->RemoveInput(arraylike_or_spread_index);
4510
4511 // Speculate on that array's map is still equal to the dynamic map of
4512 // arguments_list; generate a map check.
4513 effect = graph()->NewNode(
4514 simplified()->CheckMaps(CheckMapsFlag::kNone, ZoneRefSet<Map>(array_map),
4515 feedback_source),
4516 arguments_list, effect, control);
4517
4518 // Speculate on that array's length being equal to the dynamic length of
4519 // arguments_list; generate a deopt check.
4520 ElementsKind elements_kind = array_map.elements_kind();
4521 effect = CheckArrayLength(arguments_list, elements_kind, array_length,
4522 feedback_source, effect, control);
4523
4524 // Generate N element loads to replace the {arguments_list} node with a set
4525 // of arguments loaded from it.
4526 Node* elements = effect = graph()->NewNode(
4528 arguments_list, effect, control);
4529 for (int i = 0; i < array_length; i++) {
4530 // Load the i-th element from the array.
4531 Node* index = jsgraph()->ConstantNoHole(i);
4532 Node* load = effect = graph()->NewNode(
4533 simplified()->LoadElement(
4535 elements, index, effect, control);
4536
4537 // In "holey" arrays some arguments might be missing and we pass
4538 // 'undefined' instead.
4539 if (IsHoleyElementsKind(elements_kind)) {
4540 load = ConvertHoleToUndefined(load, elements_kind);
4541 }
4542
4543 node->InsertInput(graph()->zone(), arraylike_or_spread_index + i, load);
4544 }
4545
4547 node,
4548 javascript()->Call(JSCallNode::ArityForArgc(new_argument_count),
4549 frequency, feedback_source, ConvertReceiverMode::kAny,
4550 speculation_mode, CallFeedbackRelation::kUnrelated));
4552 return Changed(node).FollowedBy(ReduceJSCall(node));
4553}
4554
4556 // TODO(neis): Add a way to check if function template info isn't serialized
4557 // and add a warning in such cases. Currently we can't tell if function
4558 // template info doesn't exist or wasn't serialized.
4559 return function.shared(broker()).HasBuiltinId() ||
4560 function.shared(broker()).function_template_info(broker()).has_value();
4561}
4562
4564 if (broker()->StackHasOverflowed()) return NoChange();
4565
4566 JSCallNode n(node);
4567 CallParameters const& p = n.Parameters();
4568 Node* target = n.target();
4569 Effect effect = n.effect();
4570 Control control = n.control();
4571 int arity = p.arity_without_implicit_args();
4572
4573 // Try to specialize JSCall {node}s with constant {target}s.
4574 HeapObjectMatcher m(target);
4575 if (m.HasResolvedValue()) {
4576 ObjectRef target_ref = m.Ref(broker());
4577 if (target_ref.IsJSFunction()) {
4578 JSFunctionRef function = target_ref.AsJSFunction();
4579
4580 // Don't inline cross native context.
4581 if (!function.native_context(broker()).equals(native_context())) {
4582 return NoChange();
4583 }
4584
4585 return ReduceJSCall(node, function.shared(broker()));
4586 } else if (target_ref.IsJSBoundFunction()) {
4587 JSBoundFunctionRef function = target_ref.AsJSBoundFunction();
4588 ObjectRef bound_this = function.bound_this(broker());
4589 ConvertReceiverMode const convert_mode =
4590 bound_this.IsNullOrUndefined()
4593
4594 // TODO(jgruber): Inline this block below once TryGet is guaranteed to
4595 // succeed.
4596 FixedArrayRef bound_arguments = function.bound_arguments(broker());
4597 const uint32_t bound_arguments_length = bound_arguments.length();
4598 if (arity + bound_arguments_length + /* receiver */ 1 >
4600 return NoChange();
4601 }
4602 static constexpr int kInlineSize = 16; // Arbitrary.
4604 for (uint32_t i = 0; i < bound_arguments_length; ++i) {
4605 OptionalObjectRef maybe_arg = bound_arguments.TryGet(broker(), i);
4606 if (!maybe_arg.has_value()) {
4607 TRACE_BROKER_MISSING(broker(), "bound argument");
4608 return NoChange();
4609 }
4610 args.emplace_back(
4611 jsgraph()->ConstantNoHole(maybe_arg.value(), broker()));
4612 }
4613
4614 // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]].
4616 node,
4617 jsgraph()->ConstantNoHole(function.bound_target_function(broker()),
4618 broker()),
4619 JSCallNode::TargetIndex());
4621 node, jsgraph()->ConstantNoHole(bound_this, broker()),
4622 JSCallNode::ReceiverIndex());
4623
4624 // Insert the [[BoundArguments]] for {node}.
4625 for (uint32_t i = 0; i < bound_arguments_length; ++i) {
4626 node->InsertInput(graph()->zone(), i + 2, args[i]);
4627 arity++;
4628 }
4629
4631 node,
4633 p.feedback(), convert_mode, p.speculation_mode(),
4635
4636 // Try to further reduce the JSCall {node}.
4637 return Changed(node).FollowedBy(ReduceJSCall(node));
4638 }
4639
4640 // Don't mess with other {node}s that have a constant {target}.
4641 // TODO(bmeurer): Also support proxies here.
4642 return NoChange();
4643 }
4644
4645 // If {target} is the result of a JSCreateClosure operation, we can
4646 // just immediately try to inline based on the SharedFunctionInfo,
4647 // since TurboFan generally doesn't inline cross-context, and hence
4648 // the {target} must have the same native context as the call site.
4649 // Same if the {target} is the result of a CheckClosure operation.
4650 if (target->opcode() == IrOpcode::kJSCreateClosure) {
4651 CreateClosureParameters const& params =
4653 return ReduceJSCall(node, params.shared_info());
4654 } else if (target->opcode() == IrOpcode::kCheckClosure) {
4655 FeedbackCellRef cell = MakeRef(broker(), FeedbackCellOf(target->op()));
4656 OptionalSharedFunctionInfoRef shared = cell.shared_function_info(broker());
4657 if (!shared.has_value()) {
4658 TRACE_BROKER_MISSING(broker(), "Unable to reduce JSCall. FeedbackCell "
4659 << cell << " has no FeedbackVector");
4660 return NoChange();
4661 }
4662 return ReduceJSCall(node, *shared);
4663 }
4664
4665 // If {target} is the result of a JSCreateBoundFunction operation,
4666 // we can just fold the construction and call the bound target
4667 // function directly instead.
4668 if (target->opcode() == IrOpcode::kJSCreateBoundFunction) {
4669 Node* bound_target_function = NodeProperties::GetValueInput(target, 0);
4670 Node* bound_this = NodeProperties::GetValueInput(target, 1);
4671 uint32_t const bound_arguments_length =
4672 static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity());
4673
4674 // Patch the {node} to use [[BoundTargetFunction]] and [[BoundThis]].
4675 NodeProperties::ReplaceValueInput(node, bound_target_function,
4676 n.TargetIndex());
4677 NodeProperties::ReplaceValueInput(node, bound_this, n.ReceiverIndex());
4678
4679 // Insert the [[BoundArguments]] for {node}.
4680 for (uint32_t i = 0; i < bound_arguments_length; ++i) {
4681 Node* value = NodeProperties::GetValueInput(target, 2 + i);
4682 node->InsertInput(graph()->zone(), n.ArgumentIndex(i), value);
4683 arity++;
4684 }
4685
4686 // Update the JSCall operator on {node}.
4687 ConvertReceiverMode const convert_mode =
4688 NodeProperties::CanBeNullOrUndefined(broker(), bound_this, effect)
4692 node,
4694 p.feedback(), convert_mode, p.speculation_mode(),
4696
4697 // Try to further reduce the JSCall {node}.
4698 return Changed(node).FollowedBy(ReduceJSCall(node));
4699 }
4700
4701 if (!ShouldUseCallICFeedback(target) ||
4703 !p.feedback().IsValid()) {
4704 return NoChange();
4705 }
4706
4707 ProcessedFeedback const& feedback =
4709 if (feedback.IsInsufficient()) {
4711 node, DeoptimizeReason::kInsufficientTypeFeedbackForCall);
4712 }
4713
4714 OptionalHeapObjectRef feedback_target;
4716 feedback_target = feedback.AsCall().target();
4717 } else {
4719 feedback_target = native_context().function_prototype_apply(broker());
4720 }
4721
4722 if (feedback_target.has_value() &&
4723 feedback_target->map(broker()).is_callable()) {
4724 Node* target_function =
4725 jsgraph()->ConstantNoHole(*feedback_target, broker());
4726
4727 // Check that the {target} is still the {target_function}.
4728 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
4729 target_function);
4730 effect = graph()->NewNode(
4731 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
4732 effect, control);
4733
4734 // Specialize the JSCall node to the {target_function}.
4735 NodeProperties::ReplaceValueInput(node, target_function, n.TargetIndex());
4737
4738 // Try to further reduce the JSCall {node}.
4739 return Changed(node).FollowedBy(ReduceJSCall(node));
4740 } else if (feedback_target.has_value() && feedback_target->IsFeedbackCell()) {
4741 FeedbackCellRef feedback_cell = feedback_target.value().AsFeedbackCell();
4742 // TODO(neis): This check seems unnecessary.
4743 if (feedback_cell.feedback_vector(broker()).has_value()) {
4744 // Check that {target} is a closure with given {feedback_cell},
4745 // which uniquely identifies a given function inside a native context.
4746 Node* target_closure = effect =
4747 graph()->NewNode(simplified()->CheckClosure(feedback_cell.object()),
4748 target, effect, control);
4749
4750 // Specialize the JSCall node to the {target_closure}.
4751 NodeProperties::ReplaceValueInput(node, target_closure, n.TargetIndex());
4753
4754 // Try to further reduce the JSCall {node}.
4755 return Changed(node).FollowedBy(ReduceJSCall(node));
4756 }
4757 }
4758 return NoChange();
4759}
4760
4762 SharedFunctionInfoRef shared) {
4763 JSCallNode n(node);
4764 Node* target = n.target();
4765
4766 // Do not reduce calls to functions with break points.
4767 // If this state changes during background compilation, the compilation
4768 // job will be aborted from the main thread (see
4769 // Debug::PrepareFunctionForDebugExecution()).
4770 if (shared.HasBreakInfo(broker())) return NoChange();
4771
4772 // Class constructors are callable, but [[Call]] will raise an exception.
4773 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
4774 if (IsClassConstructor(shared.kind())) {
4777 node, javascript()->CallRuntime(
4778 Runtime::kThrowConstructorNonCallableError, 1));
4779 return Changed(node);
4780 }
4781
4782 // Check for known builtin functions.
4783
4784 Builtin builtin =
4785 shared.HasBuiltinId() ? shared.builtin_id() : Builtin::kNoBuiltinId;
4786 switch (builtin) {
4787 case Builtin::kArrayConstructor:
4788 return ReduceArrayConstructor(node);
4789 case Builtin::kBooleanConstructor:
4790 return ReduceBooleanConstructor(node);
4791 case Builtin::kFunctionPrototypeApply:
4792 return ReduceFunctionPrototypeApply(node);
4793 case Builtin::kFastFunctionPrototypeBind:
4794 return ReduceFunctionPrototypeBind(node);
4795 case Builtin::kFunctionPrototypeCall:
4796 return ReduceFunctionPrototypeCall(node);
4797 case Builtin::kFunctionPrototypeHasInstance:
4799 case Builtin::kObjectConstructor:
4800 return ReduceObjectConstructor(node);
4801 case Builtin::kObjectCreate:
4802 return ReduceObjectCreate(node);
4803 case Builtin::kObjectGetPrototypeOf:
4804 return ReduceObjectGetPrototypeOf(node);
4805 case Builtin::kObjectIs:
4806 return ReduceObjectIs(node);
4807 case Builtin::kObjectPrototypeGetProto:
4808 return ReduceObjectPrototypeGetProto(node);
4809 case Builtin::kObjectPrototypeHasOwnProperty:
4811 case Builtin::kObjectPrototypeIsPrototypeOf:
4813 case Builtin::kReflectApply:
4814 return ReduceReflectApply(node);
4815 case Builtin::kReflectConstruct:
4816 return ReduceReflectConstruct(node);
4817 case Builtin::kReflectGet:
4818 return ReduceReflectGet(node);
4819 case Builtin::kReflectGetPrototypeOf:
4820 return ReduceReflectGetPrototypeOf(node);
4821 case Builtin::kReflectHas:
4822 return ReduceReflectHas(node);
4823 case Builtin::kArrayForEach:
4824 return ReduceArrayForEach(node, shared);
4825 case Builtin::kArrayMap:
4826 return ReduceArrayMap(node, shared);
4827 case Builtin::kArrayFilter:
4828 return ReduceArrayFilter(node, shared);
4829 case Builtin::kArrayReduce:
4830 return ReduceArrayReduce(node, shared);
4831 case Builtin::kArrayReduceRight:
4832 return ReduceArrayReduceRight(node, shared);
4833 case Builtin::kArrayPrototypeFind:
4834 return ReduceArrayFind(node, shared);
4835 case Builtin::kArrayPrototypeFindIndex:
4836 return ReduceArrayFindIndex(node, shared);
4837 case Builtin::kArrayEvery:
4838 return ReduceArrayEvery(node, shared);
4839 case Builtin::kArrayIndexOf:
4840 return ReduceArrayIndexOf(node);
4841 case Builtin::kArrayIncludes:
4842 return ReduceArrayIncludes(node);
4843 case Builtin::kArraySome:
4844 return ReduceArraySome(node, shared);
4845 case Builtin::kArrayPrototypeAt:
4846 return ReduceArrayPrototypeAt(node);
4847 case Builtin::kArrayPrototypePush:
4848 return ReduceArrayPrototypePush(node);
4849 case Builtin::kArrayPrototypePop:
4850 return ReduceArrayPrototypePop(node);
4851 // TODO(v8:14409): The current implementation of the inlined
4852 // ArrayPrototypeShift version doesn't seem to be beneficial and even
4853 // counter-productive at least for Object ElementsKinds. Disable it until
4854 // improvements/better heuristics have been implemented.
4855 // case Builtin::kArrayPrototypeShift:
4856 // return ReduceArrayPrototypeShift(node);
4857 case Builtin::kArrayPrototypeSlice:
4858 return ReduceArrayPrototypeSlice(node);
4859 case Builtin::kArrayPrototypeEntries:
4862 case Builtin::kArrayPrototypeKeys:
4865 case Builtin::kArrayPrototypeValues:
4868 case Builtin::kArrayIteratorPrototypeNext:
4870 case Builtin::kArrayIsArray:
4871 return ReduceArrayIsArray(node);
4872 case Builtin::kArrayBufferIsView:
4873 return ReduceArrayBufferIsView(node);
4874 case Builtin::kDataViewPrototypeGetByteLength:
4875 // TODO(v8:11111): Optimize for JS_RAB_GSAB_DATA_VIEW_TYPE too.
4876 return ReduceArrayBufferViewByteLengthAccessor(node, JS_DATA_VIEW_TYPE,
4877 builtin);
4878 case Builtin::kDataViewPrototypeGetByteOffset:
4879 // TODO(v8:11111): Optimize for JS_RAB_GSAB_DATA_VIEW_TYPE too.
4880 return ReduceArrayBufferViewByteOffsetAccessor(node, JS_DATA_VIEW_TYPE,
4881 builtin);
4882 case Builtin::kDataViewPrototypeGetUint8:
4885 case Builtin::kDataViewPrototypeGetInt8:
4888 case Builtin::kDataViewPrototypeGetUint16:
4891 case Builtin::kDataViewPrototypeGetInt16:
4894 case Builtin::kDataViewPrototypeGetUint32:
4897 case Builtin::kDataViewPrototypeGetInt32:
4900 case Builtin::kDataViewPrototypeGetFloat16:
4903 case Builtin::kDataViewPrototypeGetFloat32:
4906 case Builtin::kDataViewPrototypeGetFloat64:
4909 case Builtin::kDataViewPrototypeGetBigInt64:
4912 case Builtin::kDataViewPrototypeGetBigUint64:
4915 case Builtin::kDataViewPrototypeSetUint8:
4918 case Builtin::kDataViewPrototypeSetInt8:
4921 case Builtin::kDataViewPrototypeSetUint16:
4924 case Builtin::kDataViewPrototypeSetInt16:
4927 case Builtin::kDataViewPrototypeSetUint32:
4930 case Builtin::kDataViewPrototypeSetInt32:
4933 case Builtin::kDataViewPrototypeSetFloat16:
4936 case Builtin::kDataViewPrototypeSetFloat32:
4939 case Builtin::kDataViewPrototypeSetFloat64:
4942 case Builtin::kDataViewPrototypeSetBigInt64:
4945 case Builtin::kDataViewPrototypeSetBigUint64:
4948 case Builtin::kTypedArrayPrototypeByteLength:
4949 return ReduceArrayBufferViewByteLengthAccessor(node, JS_TYPED_ARRAY_TYPE,
4950 builtin);
4951 case Builtin::kTypedArrayPrototypeByteOffset:
4952 return ReduceArrayBufferViewByteOffsetAccessor(node, JS_TYPED_ARRAY_TYPE,
4953 builtin);
4954 case Builtin::kTypedArrayPrototypeLength:
4956 case Builtin::kTypedArrayPrototypeToStringTag:
4958 case Builtin::kMathAbs:
4959 return ReduceMathUnary(node, simplified()->NumberAbs());
4960 case Builtin::kMathAcos:
4961 return ReduceMathUnary(node, simplified()->NumberAcos());
4962 case Builtin::kMathAcosh:
4963 return ReduceMathUnary(node, simplified()->NumberAcosh());
4964 case Builtin::kMathAsin:
4965 return ReduceMathUnary(node, simplified()->NumberAsin());
4966 case Builtin::kMathAsinh:
4967 return ReduceMathUnary(node, simplified()->NumberAsinh());
4968 case Builtin::kMathAtan:
4969 return ReduceMathUnary(node, simplified()->NumberAtan());
4970 case Builtin::kMathAtanh:
4971 return ReduceMathUnary(node, simplified()->NumberAtanh());
4972 case Builtin::kMathCbrt:
4973 return ReduceMathUnary(node, simplified()->NumberCbrt());
4974 case Builtin::kMathCeil:
4975 return ReduceMathUnary(node, simplified()->NumberCeil());
4976 case Builtin::kMathCos:
4977 return ReduceMathUnary(node, simplified()->NumberCos());
4978 case Builtin::kMathCosh:
4979 return ReduceMathUnary(node, simplified()->NumberCosh());
4980 case Builtin::kMathExp:
4981 return ReduceMathUnary(node, simplified()->NumberExp());
4982 case Builtin::kMathExpm1:
4983 return ReduceMathUnary(node, simplified()->NumberExpm1());
4984 case Builtin::kMathFloor:
4985 return ReduceMathUnary(node, simplified()->NumberFloor());
4986 case Builtin::kMathFround:
4987 return ReduceMathUnary(node, simplified()->NumberFround());
4988 case Builtin::kMathLog:
4989 return ReduceMathUnary(node, simplified()->NumberLog());
4990 case Builtin::kMathLog1p:
4991 return ReduceMathUnary(node, simplified()->NumberLog1p());
4992 case Builtin::kMathLog10:
4993 return ReduceMathUnary(node, simplified()->NumberLog10());
4994 case Builtin::kMathLog2:
4995 return ReduceMathUnary(node, simplified()->NumberLog2());
4996 case Builtin::kMathRound:
4997 return ReduceMathUnary(node, simplified()->NumberRound());
4998 case Builtin::kMathSign:
4999 return ReduceMathUnary(node, simplified()->NumberSign());
5000 case Builtin::kMathSin:
5001 return ReduceMathUnary(node, simplified()->NumberSin());
5002 case Builtin::kMathSinh:
5003 return ReduceMathUnary(node, simplified()->NumberSinh());
5004 case Builtin::kMathSqrt:
5005 return ReduceMathUnary(node, simplified()->NumberSqrt());
5006 case Builtin::kMathTan:
5007 return ReduceMathUnary(node, simplified()->NumberTan());
5008 case Builtin::kMathTanh:
5009 return ReduceMathUnary(node, simplified()->NumberTanh());
5010 case Builtin::kMathTrunc:
5011 return ReduceMathUnary(node, simplified()->NumberTrunc());
5012 case Builtin::kMathAtan2:
5013 return ReduceMathBinary(node, simplified()->NumberAtan2());
5014 case Builtin::kMathPow:
5015 return ReduceMathBinary(node, simplified()->NumberPow());
5016 case Builtin::kMathClz32:
5017 return ReduceMathClz32(node);
5018 case Builtin::kMathImul:
5019 return ReduceMathImul(node);
5020 case Builtin::kMathMax:
5021 return ReduceMathMinMax(node, simplified()->NumberMax(),
5022 jsgraph()->ConstantNoHole(-V8_INFINITY));
5023 case Builtin::kMathMin:
5024 return ReduceMathMinMax(node, simplified()->NumberMin(),
5025 jsgraph()->ConstantNoHole(V8_INFINITY));
5026 case Builtin::kNumberIsFinite:
5027 return ReduceNumberIsFinite(node);
5028 case Builtin::kNumberIsInteger:
5029 return ReduceNumberIsInteger(node);
5030 case Builtin::kNumberIsSafeInteger:
5031 return ReduceNumberIsSafeInteger(node);
5032 case Builtin::kNumberIsNaN:
5033 return ReduceNumberIsNaN(node);
5034 case Builtin::kNumberParseInt:
5035 return ReduceNumberParseInt(node);
5036 case Builtin::kGlobalIsFinite:
5037 return ReduceGlobalIsFinite(node);
5038 case Builtin::kGlobalIsNaN:
5039 return ReduceGlobalIsNaN(node);
5040 case Builtin::kMapPrototypeGet:
5041 return ReduceMapPrototypeGet(node);
5042 case Builtin::kMapPrototypeHas:
5043 return ReduceMapPrototypeHas(node);
5044 case Builtin::kSetPrototypeHas:
5045 return ReduceSetPrototypeHas(node);
5046 case Builtin::kRegExpPrototypeTest:
5047 return ReduceRegExpPrototypeTest(node);
5048 case Builtin::kReturnReceiver:
5049 return ReduceReturnReceiver(node);
5050 case Builtin::kStringPrototypeIndexOf:
5053 case Builtin::kStringPrototypeIncludes:
5056 case Builtin::kStringPrototypeCharAt:
5057 return ReduceStringPrototypeCharAt(node);
5058 case Builtin::kStringPrototypeCharCodeAt:
5059 return ReduceStringPrototypeStringAt(simplified()->StringCharCodeAt(),
5060 node);
5061 case Builtin::kStringPrototypeCodePointAt:
5062 return ReduceStringPrototypeStringAt(simplified()->StringCodePointAt(),
5063 node);
5064 case Builtin::kStringPrototypeSubstring:
5065 return ReduceStringPrototypeSubstring(node);
5066 case Builtin::kStringPrototypeSlice:
5067 return ReduceStringPrototypeSlice(node);
5068 case Builtin::kStringPrototypeSubstr:
5069 return ReduceStringPrototypeSubstr(node);
5070 case Builtin::kStringPrototypeStartsWith:
5072 case Builtin::kStringPrototypeEndsWith:
5073 return ReduceStringPrototypeEndsWith(node);
5074#ifdef V8_INTL_SUPPORT
5075 case Builtin::kStringPrototypeLocaleCompareIntl:
5076 return ReduceStringPrototypeLocaleCompareIntl(node);
5077 case Builtin::kStringPrototypeToLowerCaseIntl:
5078 return ReduceStringPrototypeToLowerCaseIntl(node);
5079 case Builtin::kStringPrototypeToUpperCaseIntl:
5080 return ReduceStringPrototypeToUpperCaseIntl(node);
5081#endif // V8_INTL_SUPPORT
5082 case Builtin::kStringFromCharCode:
5083 return ReduceStringFromCharCode(node);
5084 case Builtin::kStringFromCodePoint:
5085 return ReduceStringFromCodePoint(node);
5086 case Builtin::kStringPrototypeIterator:
5087 return ReduceStringPrototypeIterator(node);
5088 case Builtin::kStringIteratorPrototypeNext:
5090 case Builtin::kStringPrototypeConcat:
5091 return ReduceStringPrototypeConcat(node);
5092 case Builtin::kTypedArrayPrototypeEntries:
5095 case Builtin::kTypedArrayPrototypeKeys:
5098 case Builtin::kTypedArrayPrototypeValues:
5101 case Builtin::kPromisePrototypeCatch:
5102 return ReducePromisePrototypeCatch(node);
5103 case Builtin::kPromisePrototypeFinally:
5104 return ReducePromisePrototypeFinally(node);
5105 case Builtin::kPromisePrototypeThen:
5106 return ReducePromisePrototypeThen(node);
5107 case Builtin::kPromiseResolveTrampoline:
5108 return ReducePromiseResolveTrampoline(node);
5109 case Builtin::kMapPrototypeEntries:
5112 case Builtin::kMapPrototypeKeys:
5115 case Builtin::kMapPrototypeGetSize:
5117 case Builtin::kMapPrototypeValues:
5120 case Builtin::kMapIteratorPrototypeNext:
5122 node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(),
5123 FIRST_JS_MAP_ITERATOR_TYPE, LAST_JS_MAP_ITERATOR_TYPE);
5124 case Builtin::kSetPrototypeEntries:
5127 case Builtin::kSetPrototypeGetSize:
5129 case Builtin::kSetPrototypeValues:
5132 case Builtin::kSetIteratorPrototypeNext:
5134 node, OrderedHashSet::kEntrySize, factory()->empty_ordered_hash_set(),
5135 FIRST_JS_SET_ITERATOR_TYPE, LAST_JS_SET_ITERATOR_TYPE);
5136 case Builtin::kDatePrototypeGetTime:
5137 return ReduceDatePrototypeGetTime(node);
5138 case Builtin::kDateNow:
5139 return ReduceDateNow(node);
5140 case Builtin::kNumberConstructor:
5141 return ReduceNumberConstructor(node);
5142 case Builtin::kBigIntConstructor:
5143 return ReduceBigIntConstructor(node);
5144 case Builtin::kBigIntAsIntN:
5145 case Builtin::kBigIntAsUintN:
5146 return ReduceBigIntAsN(node, builtin);
5147#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
5148 case Builtin::kGetContinuationPreservedEmbedderData:
5149 return ReduceGetContinuationPreservedEmbedderData(node);
5150 case Builtin::kSetContinuationPreservedEmbedderData:
5151 return ReduceSetContinuationPreservedEmbedderData(node);
5152#endif // V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
5153 default:
5154 break;
5155 }
5156
5157 if (shared.function_template_info(broker()).has_value()) {
5158 return ReduceCallApiFunction(node, shared);
5159 }
5160
5161#if V8_ENABLE_WEBASSEMBLY
5162 if ((flags() & kInlineJSToWasmCalls) &&
5163 // Peek at the trusted object; ReduceCallWasmFunction will do that again
5164 // and crash if this is not a WasmExportedFunctionData any more then.
5165 IsWasmExportedFunctionData(shared.object()->GetTrustedData())) {
5166 return ReduceCallWasmFunction(node, shared);
5167 }
5168#endif // V8_ENABLE_WEBASSEMBLY
5169
5170 return NoChange();
5171}
5172
5174 std::unordered_set<Node*>* generated_calls_with_array_like_or_spread) {
5175 DCHECK_EQ(generated_calls_with_array_like_or_spread->count(node_ptr()), 0);
5177 CallParameters const& p = n.Parameters();
5178 TNode<Object> arguments_list = n.LastArgument();
5179 DCHECK_EQ(static_cast<Node*>(arguments_list)->opcode(),
5180 IrOpcode::kJSCreateEmptyLiteralArray);
5181
5182 // Check that arguments_list's prototype is still an array prototype.
5186 TNode<HeapObject> initial_array_prototype =
5188 ->target_native_context()
5189 .initial_array_prototype(broker())
5190 .object());
5191 TNode<Boolean> check = ReferenceEqual(proto, initial_array_prototype);
5192 CheckIf(check, DeoptimizeReason::kWrongMap, p.feedback());
5193
5194 // Turn the JSCallWithArrayLike or JSCallWithSpread roughly into:
5195 //
5196 // "arguments_list array is still empty?"
5197 // |
5198 // |
5199 // Branch
5200 // / \
5201 // / \
5202 // IfTrue IfFalse
5203 // | |
5204 // | |
5205 // JSCall JSCallWithArrayLike/JSCallWithSpread
5206 // \ /
5207 // \ /
5208 // Merge
5209
5212 return SelectIf<Object>(NumberEqual(length, ZeroConstant()))
5213 .Then([&]() {
5214 TNode<Object> call = CopyNode();
5215 static_cast<Node*>(call)->RemoveInput(n.LastArgumentIndex());
5217 call, javascript()->Call(p.arity() - 1, p.frequency(), p.feedback(),
5219 p.feedback_relation()));
5220 return call;
5221 })
5222 .Else([&]() {
5223 TNode<Object> call = CopyNode();
5224 generated_calls_with_array_like_or_spread->insert(call);
5225 return call;
5226 })
5227 .ExpectFalse()
5228 .Value();
5229}
5230
5231namespace {
5232
5233// Check if the target is a class constructor.
5234// We need to check all cases where the target will be typed as Function
5235// to prevent later optimizations from using the CallFunction trampoline,
5236// skipping the instance type check.
5237bool TargetIsClassConstructor(Node* node, JSHeapBroker* broker) {
5238 Node* target = NodeProperties::GetValueInput(node, 0);
5239 OptionalSharedFunctionInfoRef shared;
5240 HeapObjectMatcher m(target);
5241 if (m.HasResolvedValue()) {
5242 ObjectRef target_ref = m.Ref(broker);
5243 if (target_ref.IsJSFunction()) {
5244 JSFunctionRef function = target_ref.AsJSFunction();
5245 shared = function.shared(broker);
5246 }
5247 } else if (target->opcode() == IrOpcode::kJSCreateClosure) {
5248 CreateClosureParameters const& ccp =
5249 JSCreateClosureNode{target}.Parameters();
5250 shared = ccp.shared_info();
5251 } else if (target->opcode() == IrOpcode::kCheckClosure) {
5252 FeedbackCellRef cell = MakeRef(broker, FeedbackCellOf(target->op()));
5253 shared = cell.shared_function_info(broker);
5254 }
5255
5256 if (shared.has_value() && IsClassConstructor(shared->kind())) return true;
5257
5258 return false;
5259}
5260
5261} // namespace
5262
5265 CallParameters const& p = n.Parameters();
5266 DCHECK_EQ(p.arity_without_implicit_args(), 1); // The arraylike object.
5267 // Class constructors are callable, but [[Call]] will raise an exception.
5268 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
5269 if (TargetIsClassConstructor(node, broker())) {
5270 return NoChange();
5271 }
5272
5273 std::optional<Reduction> maybe_result =
5275 if (maybe_result.has_value()) {
5276 return maybe_result.value();
5277 }
5278
5280 node, n.ArgumentCount(), n.LastArgumentIndex(), p.frequency(),
5281 p.feedback(), p.speculation_mode(), p.feedback_relation(), n.target(),
5282 n.effect(), n.control());
5283}
5284
5286 JSCallWithSpreadNode n(node);
5287 CallParameters const& p = n.Parameters();
5288 DCHECK_GE(p.arity_without_implicit_args(), 1); // At least the spread.
5289 // Class constructors are callable, but [[Call]] will raise an exception.
5290 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
5291 if (TargetIsClassConstructor(node, broker())) {
5292 return NoChange();
5293 }
5295 node, n.ArgumentCount(), n.LastArgumentIndex(), p.frequency(),
5296 p.feedback(), p.speculation_mode(), p.feedback_relation(), n.target(),
5297 n.effect(), n.control());
5298}
5299
5301 if (broker()->StackHasOverflowed()) return NoChange();
5302
5303 JSConstructNode n(node);
5304 ConstructParameters const& p = n.Parameters();
5305 int arity = p.arity_without_implicit_args();
5306 Node* target = n.target();
5307 Node* new_target = n.new_target();
5308 Effect effect = n.effect();
5309 Control control = n.control();
5310
5311 if (p.feedback().IsValid()) {
5312 ProcessedFeedback const& feedback =
5314 if (feedback.IsInsufficient()) {
5316 node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct);
5317 }
5318
5319 OptionalHeapObjectRef feedback_target = feedback.AsCall().target();
5320 if (feedback_target.has_value() && feedback_target->IsAllocationSite()) {
5321 // The feedback is an AllocationSite, which means we have called the
5322 // Array function and collected transition (and pretenuring) feedback
5323 // for the resulting arrays. This has to be kept in sync with the
5324 // implementation in Ignition.
5325
5326 Node* array_function = jsgraph()->ConstantNoHole(
5327 native_context().array_function(broker()), broker());
5328
5329 // Check that the {target} is still the {array_function}.
5330 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
5331 array_function);
5332 effect = graph()->NewNode(
5333 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
5334 effect, control);
5335
5336 // Turn the {node} into a {JSCreateArray} call.
5338 static_assert(JSConstructNode::NewTargetIndex() == 1);
5339 node->ReplaceInput(n.NewTargetIndex(), array_function);
5340 node->RemoveInput(n.FeedbackVectorIndex());
5342 node, javascript()->CreateArray(arity,
5343 feedback_target->AsAllocationSite()));
5344 return Changed(node);
5345 } else if (feedback_target.has_value() &&
5346 !HeapObjectMatcher(new_target).HasResolvedValue() &&
5347 feedback_target->map(broker()).is_constructor()) {
5348 Node* new_target_feedback =
5349 jsgraph()->ConstantNoHole(*feedback_target, broker());
5350
5351 // Check that the {new_target} is still the {new_target_feedback}.
5352 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), new_target,
5353 new_target_feedback);
5354 effect = graph()->NewNode(
5355 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
5356 effect, control);
5357
5358 // Specialize the JSConstruct node to the {new_target_feedback}.
5359 node->ReplaceInput(n.NewTargetIndex(), new_target_feedback);
5361 if (target == new_target) {
5362 node->ReplaceInput(n.TargetIndex(), new_target_feedback);
5363 }
5364
5365 // Try to further reduce the JSConstruct {node}.
5366 return Changed(node).FollowedBy(ReduceJSConstruct(node));
5367 }
5368 }
5369
5370 // Try to specialize JSConstruct {node}s with constant {target}s.
5371 HeapObjectMatcher m(target);
5372 if (m.HasResolvedValue()) {
5373 HeapObjectRef target_ref = m.Ref(broker());
5374
5375 // Raise a TypeError if the {target} is not a constructor.
5376 if (!target_ref.map(broker()).is_constructor()) {
5380 Runtime::kThrowConstructedNonConstructable));
5381 return Changed(node);
5382 }
5383
5384 if (target_ref.IsJSFunction()) {
5385 JSFunctionRef function = target_ref.AsJSFunction();
5386
5387 // Do not reduce constructors with break points.
5388 // If this state changes during background compilation, the compilation
5389 // job will be aborted from the main thread (see
5390 // Debug::PrepareFunctionForDebugExecution()).
5391 SharedFunctionInfoRef sfi = function.shared(broker());
5392 if (sfi.HasBreakInfo(broker())) return NoChange();
5393
5394 // Don't inline cross native context.
5395 if (!function.native_context(broker()).equals(native_context())) {
5396 return NoChange();
5397 }
5398
5399 // Check for known builtin functions.
5400 Builtin builtin =
5401 sfi.HasBuiltinId() ? sfi.builtin_id() : Builtin::kNoBuiltinId;
5402 switch (builtin) {
5403 case Builtin::kArrayConstructor: {
5404 // TODO(bmeurer): Deal with Array subclasses here.
5405 // Turn the {node} into a {JSCreateArray} call.
5406 static_assert(JSConstructNode::NewTargetIndex() == 1);
5407 node->ReplaceInput(n.NewTargetIndex(), new_target);
5408 node->RemoveInput(n.FeedbackVectorIndex());
5410 node, javascript()->CreateArray(arity, std::nullopt));
5411 return Changed(node);
5412 }
5413 case Builtin::kObjectConstructor: {
5414 // If no value is passed, we can immediately lower to a simple
5415 // JSCreate and don't need to do any massaging of the {node}.
5416 if (arity == 0) {
5417 node->RemoveInput(n.FeedbackVectorIndex());
5418 NodeProperties::ChangeOp(node, javascript()->Create());
5419 return Changed(node);
5420 }
5421
5422 // If {target} is not the same as {new_target} (i.e. the Object
5423 // constructor), {value} will be ignored and therefore we can lower
5424 // to {JSCreate}. See https://tc39.es/ecma262/#sec-object-value.
5425 HeapObjectMatcher mnew_target(new_target);
5426 if (mnew_target.HasResolvedValue() &&
5427 !mnew_target.Ref(broker()).equals(function)) {
5428 // Drop the value inputs.
5429 node->RemoveInput(n.FeedbackVectorIndex());
5430 for (int i = n.ArgumentCount() - 1; i >= 0; i--) {
5431 node->RemoveInput(n.ArgumentIndex(i));
5432 }
5433 NodeProperties::ChangeOp(node, javascript()->Create());
5434 return Changed(node);
5435 }
5436 break;
5437 }
5438 case Builtin::kPromiseConstructor:
5439 return ReducePromiseConstructor(node);
5440 case Builtin::kStringConstructor:
5441 return ReduceStringConstructor(node, function);
5442 case Builtin::kTypedArrayConstructor:
5443 return ReduceTypedArrayConstructor(node, function.shared(broker()));
5444 default:
5445 break;
5446 }
5447 } else if (target_ref.IsJSBoundFunction()) {
5448 JSBoundFunctionRef function = target_ref.AsJSBoundFunction();
5449 JSReceiverRef bound_target_function =
5450 function.bound_target_function(broker());
5451 FixedArrayRef bound_arguments = function.bound_arguments(broker());
5452 const uint32_t bound_arguments_length = bound_arguments.length();
5453
5454 // TODO(jgruber): Inline this block below once TryGet is guaranteed to
5455 // succeed.
5456 static constexpr int kInlineSize = 16; // Arbitrary.
5458 for (uint32_t i = 0; i < bound_arguments_length; ++i) {
5459 OptionalObjectRef maybe_arg = bound_arguments.TryGet(broker(), i);
5460 if (!maybe_arg.has_value()) {
5461 TRACE_BROKER_MISSING(broker(), "bound argument");
5462 return NoChange();
5463 }
5464 args.emplace_back(
5465 jsgraph()->ConstantNoHole(maybe_arg.value(), broker()));
5466 }
5467
5468 // Patch {node} to use [[BoundTargetFunction]].
5469 node->ReplaceInput(n.TargetIndex(), jsgraph()->ConstantNoHole(
5470 bound_target_function, broker()));
5471
5472 // Patch {node} to use [[BoundTargetFunction]]
5473 // as new.target if {new_target} equals {target}.
5474 if (target == new_target) {
5475 node->ReplaceInput(
5476 n.NewTargetIndex(),
5477 jsgraph()->ConstantNoHole(bound_target_function, broker()));
5478 } else {
5479 node->ReplaceInput(
5480 n.NewTargetIndex(),
5481 graph()->NewNode(
5483 graph()->NewNode(simplified()->ReferenceEqual(), target,
5484 new_target),
5485 jsgraph()->ConstantNoHole(bound_target_function, broker()),
5486 new_target));
5487 }
5488
5489 // Insert the [[BoundArguments]] for {node}.
5490 for (uint32_t i = 0; i < bound_arguments_length; ++i) {
5491 node->InsertInput(graph()->zone(), n.ArgumentIndex(i), args[i]);
5492 arity++;
5493 }
5494
5495 // Update the JSConstruct operator on {node}.
5497 node, javascript()->Construct(JSConstructNode::ArityForArgc(arity),
5498 p.frequency(), FeedbackSource()));
5499
5500 // Try to further reduce the JSConstruct {node}.
5501 return Changed(node).FollowedBy(ReduceJSConstruct(node));
5502 }
5503
5504 // TODO(bmeurer): Also support optimizing proxies here.
5505 }
5506
5507 // If {target} is the result of a JSCreateBoundFunction operation,
5508 // we can just fold the construction and construct the bound target
5509 // function directly instead.
5510 if (target->opcode() == IrOpcode::kJSCreateBoundFunction) {
5511 Node* bound_target_function = NodeProperties::GetValueInput(target, 0);
5512 uint32_t const bound_arguments_length =
5513 static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity());
5514
5515 // Patch the {node} to use [[BoundTargetFunction]].
5516 node->ReplaceInput(n.TargetIndex(), bound_target_function);
5517
5518 // Patch {node} to use [[BoundTargetFunction]]
5519 // as new.target if {new_target} equals {target}.
5520 if (target == new_target) {
5521 node->ReplaceInput(n.NewTargetIndex(), bound_target_function);
5522 } else {
5523 node->ReplaceInput(
5524 n.NewTargetIndex(),
5525 graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
5526 graph()->NewNode(simplified()->ReferenceEqual(),
5527 target, new_target),
5528 bound_target_function, new_target));
5529 }
5530
5531 // Insert the [[BoundArguments]] for {node}.
5532 for (uint32_t i = 0; i < bound_arguments_length; ++i) {
5533 Node* value = NodeProperties::GetValueInput(target, 2 + i);
5534 node->InsertInput(graph()->zone(), n.ArgumentIndex(i), value);
5535 arity++;
5536 }
5537
5538 // Update the JSConstruct operator on {node}.
5540 node, javascript()->Construct(JSConstructNode::ArityForArgc(arity),
5541 p.frequency(), FeedbackSource()));
5542
5543 // Try to further reduce the JSConstruct {node}.
5544 return Changed(node).FollowedBy(ReduceJSConstruct(node));
5545 }
5546
5547 return NoChange();
5548}
5549
5550// ES #sec-string.prototype.indexof
5551// ES #sec-string.prototype.includes
5553 Node* node, StringIndexOfIncludesVariant variant) {
5554 JSCallNode n(node);
5555 CallParameters const& p = n.Parameters();
5557 return NoChange();
5558 }
5559
5560 Effect effect = n.effect();
5561 Control control = n.control();
5562 if (n.ArgumentCount() > 0) {
5563 Node* receiver = n.receiver();
5564 Node* new_receiver = effect = graph()->NewNode(
5565 simplified()->CheckString(p.feedback()), receiver, effect, control);
5566
5567 Node* search_string = n.Argument(0);
5568 Node* new_search_string = effect =
5569 graph()->NewNode(simplified()->CheckString(p.feedback()), search_string,
5570 effect, control);
5571
5572 Node* new_position = jsgraph()->ZeroConstant();
5573 if (n.ArgumentCount() > 1) {
5574 Node* position = n.Argument(1);
5575 new_position = effect = graph()->NewNode(
5576 simplified()->CheckSmi(p.feedback()), position, effect, control);
5577
5578 Node* receiver_length =
5579 graph()->NewNode(simplified()->StringLength(), new_receiver);
5580 new_position = graph()->NewNode(
5581 simplified()->NumberMin(),
5582 graph()->NewNode(simplified()->NumberMax(), new_position,
5583 jsgraph()->ZeroConstant()),
5584 receiver_length);
5585 }
5586
5589 node->ReplaceInput(0, new_receiver);
5590 node->ReplaceInput(1, new_search_string);
5591 node->ReplaceInput(2, new_position);
5592 node->TrimInputCount(3);
5593 NodeProperties::ChangeOp(node, simplified()->StringIndexOf());
5594
5596 return Changed(node);
5597 } else {
5599 Node* result =
5600 graph()->NewNode(simplified()->BooleanNot(),
5601 graph()->NewNode(simplified()->NumberEqual(), node,
5602 jsgraph()->SmiConstant(-1)));
5603 return Replace(result);
5604 }
5605 }
5606 return NoChange();
5607}
5608
5609// ES #sec-string.prototype.substring
5611 JSCallNode n(node);
5612 CallParameters const& p = n.Parameters();
5613 if (n.ArgumentCount() < 1) return NoChange();
5615 return NoChange();
5616 }
5617
5618 JSCallReducerAssembler a(this, node);
5619 Node* subgraph = a.ReduceStringPrototypeSubstring();
5620 return ReplaceWithSubgraph(&a, subgraph);
5621}
5622
5623// ES #sec-string.prototype.slice
5625 JSCallNode n(node);
5626 CallParameters const& p = n.Parameters();
5627 if (n.ArgumentCount() < 1) return NoChange();
5629 return NoChange();
5630 }
5631
5632 JSCallReducerAssembler a(this, node);
5633 Node* subgraph = a.ReduceStringPrototypeSlice();
5634 return ReplaceWithSubgraph(&a, subgraph);
5635}
5636
5637// ES #sec-string.prototype.substr
5639 JSCallNode n(node);
5640 CallParameters const& p = n.Parameters();
5641 if (n.ArgumentCount() < 1) return NoChange();
5643 return NoChange();
5644 }
5645
5646 Effect effect = n.effect();
5647 Control control = n.control();
5648 Node* receiver = n.receiver();
5649 Node* start = n.Argument(0);
5650 Node* end = n.ArgumentOrUndefined(1, jsgraph());
5651
5652 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
5653 receiver, effect, control);
5654
5655 start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start,
5656 effect, control);
5657
5658 Node* length = graph()->NewNode(simplified()->StringLength(), receiver);
5659
5660 // Replace {end} argument with {length} if it is undefined.
5661 {
5662 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end,
5663 jsgraph()->UndefinedConstant());
5664 Node* branch =
5665 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
5666
5667 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
5668 Node* etrue = effect;
5669 Node* vtrue = length;
5670
5671 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
5672 Node* efalse = effect;
5673 Node* vfalse = efalse = graph()->NewNode(
5674 simplified()->CheckSmi(p.feedback()), end, efalse, if_false);
5675
5676 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
5677 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
5679 vtrue, vfalse, control);
5680 }
5681
5682 Node* initStart = graph()->NewNode(
5684 graph()->NewNode(simplified()->NumberLessThan(), start,
5685 jsgraph()->ZeroConstant()),
5686 graph()->NewNode(
5687 simplified()->NumberMax(),
5688 graph()->NewNode(simplified()->NumberAdd(), length, start),
5689 jsgraph()->ZeroConstant()),
5690 start);
5691 // The select above guarantees that initStart is non-negative, but
5692 // our typer can't figure that out yet.
5693 initStart = effect = graph()->NewNode(
5694 common()->TypeGuard(Type::UnsignedSmall()), initStart, effect, control);
5695
5696 Node* resultLength = graph()->NewNode(
5697 simplified()->NumberMin(),
5698 graph()->NewNode(simplified()->NumberMax(), end,
5699 jsgraph()->ZeroConstant()),
5700 graph()->NewNode(simplified()->NumberSubtract(), length, initStart));
5701
5702 // The the select below uses {resultLength} only if {resultLength > 0},
5703 // but our typer can't figure that out yet.
5704 Node* to = effect = graph()->NewNode(
5705 common()->TypeGuard(Type::UnsignedSmall()),
5706 graph()->NewNode(simplified()->NumberAdd(), initStart, resultLength),
5707 effect, control);
5708
5709 Node* result_string = nullptr;
5710 // Return empty string if {from} is smaller than {to}.
5711 {
5712 Node* check = graph()->NewNode(simplified()->NumberLessThan(),
5713 jsgraph()->ZeroConstant(), resultLength);
5714
5715 Node* branch =
5716 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
5717
5718 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
5719 Node* etrue = effect;
5720 Node* vtrue = etrue =
5721 graph()->NewNode(simplified()->StringSubstring(), receiver, initStart,
5722 to, etrue, if_true);
5723
5724 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
5725 Node* efalse = effect;
5726 Node* vfalse = jsgraph()->EmptyStringConstant();
5727
5728 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
5729 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
5730 result_string =
5732 vtrue, vfalse, control);
5733 }
5734
5735 ReplaceWithValue(node, result_string, effect, control);
5736 return Replace(result_string);
5737}
5738
5741 ConstructParameters const& p = n.Parameters();
5742 const int arraylike_index = n.LastArgumentIndex();
5743 DCHECK_EQ(n.ArgumentCount(), 1); // The arraylike object.
5745 node, n.ArgumentCount(), arraylike_index, p.frequency(), p.feedback(),
5747 n.target(), n.effect(), n.control());
5748}
5749
5752 ConstructParameters const& p = n.Parameters();
5753 const int spread_index = n.LastArgumentIndex();
5754 DCHECK_GE(n.ArgumentCount(), 1); // At least the spread.
5756 node, n.ArgumentCount(), spread_index, p.frequency(), p.feedback(),
5758 n.target(), n.effect(), n.control());
5759}
5760
5763 DCHECK_EQ(n.ArgumentCount(), 0);
5764
5765 // If this frame is not being inlined, JSConstructForwardAllArgs will be
5766 // lowered later in JSGenericLowering to a builtin call.
5767 FrameState frame_state = n.frame_state();
5768 if (frame_state.outer_frame_state()->opcode() != IrOpcode::kFrameState) {
5769 return NoChange();
5770 }
5771
5772 // Hook up the arguments directly when forwarding arguments of inlined frames.
5773 FrameState outer_state{frame_state.outer_frame_state()};
5774 FrameStateInfo outer_info = outer_state.frame_state_info();
5775 if (outer_info.type() == FrameStateType::kInlinedExtraArguments) {
5776 frame_state = outer_state;
5777 }
5778
5779 int argc = 0;
5780 StateValuesAccess parameters_access(frame_state.parameters());
5781 for (auto it = parameters_access.begin_without_receiver(); !it.done(); ++it) {
5782 DCHECK_NOT_NULL(it.node());
5783 node->InsertInput(graph()->zone(),
5784 JSCallOrConstructNode::ArgumentIndex(argc++), it.node());
5785 }
5786
5787 ConstructParameters const& p = n.Parameters();
5789 node, javascript()->Construct(JSConstructNode::ArityForArgc(argc),
5790 p.frequency(), p.feedback()));
5791 CheckIfConstructor(node);
5792 return Changed(node).FollowedBy(ReduceJSConstruct(node));
5793}
5794
5796 JSCallNode n(node);
5797 Node* receiver = n.receiver();
5799 return Replace(receiver);
5800}
5801
5803 Node* node, DeoptimizeReason reason) {
5804 DCHECK(node->opcode() == IrOpcode::kJSCall ||
5805 node->opcode() == IrOpcode::kJSConstruct);
5806 if (!(flags() & kBailoutOnUninitialized)) return NoChange();
5807
5808 Node* effect = NodeProperties::GetEffectInput(node);
5809 Node* control = NodeProperties::GetControlInput(node);
5810 Node* frame_state =
5812 Node* deoptimize =
5813 graph()->NewNode(common()->Deoptimize(reason, FeedbackSource()),
5814 frame_state, effect, control);
5815 MergeControlToEnd(graph(), common(), deoptimize);
5816 node->TrimInputCount(0);
5817 NodeProperties::ChangeOp(node, common()->Dead());
5818 return Changed(node);
5819}
5820
5822 Control control) {
5823 Node* effect_node = *effect;
5824 Node* receiver_map = effect_node =
5825 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
5826 receiver, effect_node, control);
5827 Node* receiver_bit_field2 = effect_node = graph()->NewNode(
5828 simplified()->LoadField(AccessBuilder::ForMapBitField2()), receiver_map,
5829 effect_node, control);
5830 Node* receiver_elements_kind = graph()->NewNode(
5831 simplified()->NumberShiftRightLogical(),
5832 graph()->NewNode(
5833 simplified()->NumberBitwiseAnd(), receiver_bit_field2,
5834 jsgraph()->ConstantNoHole(Map::Bits2::ElementsKindBits::kMask)),
5835 jsgraph()->ConstantNoHole(Map::Bits2::ElementsKindBits::kShift));
5836 *effect = effect_node;
5837 return receiver_elements_kind;
5838}
5839
5840void JSCallReducer::CheckIfElementsKind(Node* receiver_elements_kind,
5841 ElementsKind kind, Node* control,
5842 Node** if_true, Node** if_false) {
5843 Node* is_packed_kind =
5844 graph()->NewNode(simplified()->NumberEqual(), receiver_elements_kind,
5845 jsgraph()->ConstantNoHole(GetPackedElementsKind(kind)));
5846 Node* packed_branch =
5847 graph()->NewNode(common()->Branch(), is_packed_kind, control);
5848 Node* if_packed = graph()->NewNode(common()->IfTrue(), packed_branch);
5849
5851 Node* if_not_packed = graph()->NewNode(common()->IfFalse(), packed_branch);
5852 Node* is_holey_kind =
5853 graph()->NewNode(simplified()->NumberEqual(), receiver_elements_kind,
5854 jsgraph()->ConstantNoHole(GetHoleyElementsKind(kind)));
5855 Node* holey_branch =
5856 graph()->NewNode(common()->Branch(), is_holey_kind, if_not_packed);
5857 Node* if_holey = graph()->NewNode(common()->IfTrue(), holey_branch);
5858
5859 Node* if_not_packed_not_holey =
5860 graph()->NewNode(common()->IfFalse(), holey_branch);
5861
5862 *if_true = graph()->NewNode(common()->Merge(2), if_packed, if_holey);
5863 *if_false = if_not_packed_not_holey;
5864 } else {
5865 *if_true = if_packed;
5866 *if_false = graph()->NewNode(common()->IfFalse(), packed_branch);
5867 }
5868}
5869
5870// ES6 section 23.1.3.1 Array.prototype.at ( )
5872 if (!v8_flags.turbo_inline_array_builtins) return NoChange();
5873
5874 JSCallNode n(node);
5875 CallParameters const& p = n.Parameters();
5877 return NoChange();
5878 }
5879
5880 Node* receiver = n.receiver();
5881 Effect effect = n.effect();
5882 Control control = n.control();
5883
5884 MapInference inference(broker(), receiver, effect);
5885 if (!inference.HaveMaps()) return NoChange();
5886
5887 // Collecting maps, and checking if a fallback builtin call will be required
5888 // (it is required if at least one map doesn't support fast array iteration).
5889 ZoneVector<MapRef> maps(broker()->zone());
5890 bool needs_fallback_builtin_call = false;
5891 for (MapRef map : inference.GetMaps()) {
5892 if (map.supports_fast_array_iteration(broker())) {
5893 maps.push_back(map);
5894 } else {
5895 needs_fallback_builtin_call = true;
5896 }
5897 }
5898
5899 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
5900 control, p.feedback());
5901
5902 if (maps.empty()) {
5903 // No map in the feedback supports fast iteration. Keeping the builtin call.
5904 return NoChange();
5905 }
5906
5907 if (!dependencies()->DependOnNoElementsProtector()) {
5908 return NoChange();
5909 }
5910
5912 a.InitializeEffectControl(effect, control);
5913
5914 TNode<Object> subgraph =
5915 a.ReduceArrayPrototypeAt(maps, needs_fallback_builtin_call);
5916 return ReplaceWithSubgraph(&a, subgraph);
5917}
5918
5919// ES6 section 22.1.3.18 Array.prototype.push ( )
5921 JSCallNode n(node);
5922 CallParameters const& p = n.Parameters();
5924 return NoChange();
5925 }
5926
5927 Node* receiver = n.receiver();
5928 Effect effect = n.effect();
5929 Control control = n.control();
5930
5931 MapInference inference(broker(), receiver, effect);
5932 if (!inference.HaveMaps()) return NoChange();
5933 ZoneRefSet<Map> const& receiver_maps = inference.GetMaps();
5934
5935 std::vector<ElementsKind> kinds;
5936 if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kinds, true)) {
5937 return inference.NoChange();
5938 }
5939 if (!dependencies()->DependOnNoElementsProtector()) {
5940 return inference.NoChange();
5941 }
5942
5943 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
5944 control, p.feedback());
5945
5947 a.InitializeEffectControl(effect, control);
5948
5949 TNode<Object> subgraph = a.ReduceArrayPrototypePush(&inference);
5950 return ReplaceWithSubgraph(&a, subgraph);
5951}
5952
5953// ES6 section 22.1.3.17 Array.prototype.pop ( )
5955 JSCallNode n(node);
5956 CallParameters const& p = n.Parameters();
5958 return NoChange();
5959 }
5960
5961 Effect effect = n.effect();
5962 Control control = n.control();
5963 Node* receiver = n.receiver();
5964
5965 MapInference inference(broker(), receiver, effect);
5966 if (!inference.HaveMaps()) return NoChange();
5967 ZoneRefSet<Map> const& receiver_maps = inference.GetMaps();
5968
5969 std::vector<ElementsKind> kinds;
5970 if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kinds)) {
5971 return inference.NoChange();
5972 }
5973 if (!dependencies()->DependOnNoElementsProtector()) {
5974 return inference.NoChange();
5975 }
5976 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
5977 control, p.feedback());
5978
5979 std::vector<Node*> controls_to_merge;
5980 std::vector<Node*> effects_to_merge;
5981 std::vector<Node*> values_to_merge;
5982 Node* value = jsgraph()->UndefinedConstant();
5983
5984 Node* receiver_elements_kind =
5985 LoadReceiverElementsKind(receiver, &effect, control);
5986 Node* next_control = control;
5987 Node* next_effect = effect;
5988 for (size_t i = 0; i < kinds.size(); i++) {
5989 ElementsKind kind = kinds[i];
5990 control = next_control;
5991 effect = next_effect;
5992 // We do not need branch for the last elements kind.
5993 if (i != kinds.size() - 1) {
5994 Node* control_node = control;
5995 CheckIfElementsKind(receiver_elements_kind, kind, control_node,
5996 &control_node, &next_control);
5997 control = control_node;
5998 }
5999
6000 // Load the "length" property of the {receiver}.
6001 Node* length = effect = graph()->NewNode(
6003 receiver, effect, control);
6004
6005 // Check if the {receiver} has any elements.
6006 Node* check = graph()->NewNode(simplified()->NumberEqual(), length,
6007 jsgraph()->ZeroConstant());
6008 Node* branch =
6009 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
6010
6011 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
6012 Node* etrue = effect;
6013 Node* vtrue = jsgraph()->UndefinedConstant();
6014
6015 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
6016 Node* efalse = effect;
6017 Node* vfalse;
6018 {
6019 // TODO(turbofan): We should trim the backing store if the capacity is too
6020 // big, as implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
6021
6022 // Load the elements backing store from the {receiver}.
6023 Node* elements = efalse = graph()->NewNode(
6025 receiver, efalse, if_false);
6026
6027 // Ensure that we aren't popping from a copy-on-write backing store.
6029 elements = efalse =
6030 graph()->NewNode(simplified()->EnsureWritableFastElements(),
6031 receiver, elements, efalse, if_false);
6032 }
6033
6034 // Compute the new {length}.
6035 Node* new_length = graph()->NewNode(simplified()->NumberSubtract(),
6036 length, jsgraph()->OneConstant());
6037
6038 if (v8_flags.turbo_typer_hardening) {
6039 new_length = efalse = graph()->NewNode(
6042 new_length, length, efalse, if_false);
6043 }
6044
6045 // Store the new {length} to the {receiver}.
6046 efalse = graph()->NewNode(
6048 receiver, new_length, efalse, if_false);
6049
6050 // Load the last entry from the {elements}.
6051 vfalse = efalse = graph()->NewNode(
6053 elements, new_length, efalse, if_false);
6054
6055 // Store a hole to the element we just removed from the {receiver}.
6056 efalse = graph()->NewNode(
6057 simplified()->StoreElement(
6059 elements, new_length, jsgraph()->TheHoleConstant(), efalse, if_false);
6060 }
6061
6062 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
6063 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
6064 value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
6065 vtrue, vfalse, control);
6066
6067 // Convert the hole to undefined. Do this last, so that we can optimize
6068 // conversion operator via some smart strength reduction in many cases.
6070 value =
6071 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
6072 }
6073
6074 controls_to_merge.push_back(control);
6075 effects_to_merge.push_back(effect);
6076 values_to_merge.push_back(value);
6077 }
6078
6079 if (controls_to_merge.size() > 1) {
6080 int const count = static_cast<int>(controls_to_merge.size());
6081
6082 control = graph()->NewNode(common()->Merge(count), count,
6083 &controls_to_merge.front());
6084 effects_to_merge.push_back(control);
6085 effect = graph()->NewNode(common()->EffectPhi(count), count + 1,
6086 &effects_to_merge.front());
6087 values_to_merge.push_back(control);
6088 value =
6090 count + 1, &values_to_merge.front());
6091 }
6092
6093 ReplaceWithValue(node, value, effect, control);
6094 return Replace(value);
6095}
6096
6097// ES6 section 22.1.3.22 Array.prototype.shift ( )
6098// Currently disabled. See https://crbug.com/v8/14409
6100 JSCallNode n(node);
6101 CallParameters const& p = n.Parameters();
6103 return NoChange();
6104 }
6105
6106 Node* target = n.target();
6107 Node* receiver = n.receiver();
6108 Node* context = n.context();
6109 FrameState frame_state = n.frame_state();
6110 Effect effect = n.effect();
6111 Control control = n.control();
6112
6113 MapInference inference(broker(), receiver, effect);
6114 if (!inference.HaveMaps()) return NoChange();
6115 ZoneRefSet<Map> const& receiver_maps = inference.GetMaps();
6116
6117 std::vector<ElementsKind> kinds;
6118 if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kinds)) {
6119 return inference.NoChange();
6120 }
6121 if (!dependencies()->DependOnNoElementsProtector()) {
6122 return inference.NoChange();
6123 }
6124 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
6125 control, p.feedback());
6126
6127 std::vector<Node*> controls_to_merge;
6128 std::vector<Node*> effects_to_merge;
6129 std::vector<Node*> values_to_merge;
6130 Node* value = jsgraph()->UndefinedConstant();
6131
6132 Node* receiver_elements_kind =
6133 LoadReceiverElementsKind(receiver, &effect, control);
6134 Node* next_control = control;
6135 Node* next_effect = effect;
6136 for (size_t i = 0; i < kinds.size(); i++) {
6137 ElementsKind kind = kinds[i];
6138 control = next_control;
6139 effect = next_effect;
6140 // We do not need branch for the last elements kind.
6141 if (i != kinds.size() - 1) {
6142 Node* control_node = control;
6143 CheckIfElementsKind(receiver_elements_kind, kind, control_node,
6144 &control_node, &next_control);
6145 control = control_node;
6146 }
6147
6148 // Load length of the {receiver}.
6149 Node* length = effect = graph()->NewNode(
6151 receiver, effect, control);
6152
6153 // Return undefined if {receiver} has no elements.
6154 Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length,
6155 jsgraph()->ZeroConstant());
6156 Node* branch0 =
6157 graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
6158
6159 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
6160 Node* etrue0 = effect;
6161 Node* vtrue0 = jsgraph()->UndefinedConstant();
6162
6163 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
6164 Node* efalse0 = effect;
6165 Node* vfalse0;
6166 {
6167 // Check if we should take the fast-path.
6168 Node* check1 = graph()->NewNode(
6169 simplified()->NumberLessThanOrEqual(), length,
6170 jsgraph()->ConstantNoHole(JSArray::kMaxCopyElements));
6171 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
6172 check1, if_false0);
6173
6174 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
6175 Node* etrue1 = efalse0;
6176 Node* vtrue1;
6177 {
6178 Node* elements = etrue1 = graph()->NewNode(
6180 receiver, etrue1, if_true1);
6181
6182 // Load the first element here, which we return below.
6183 vtrue1 = etrue1 = graph()->NewNode(
6184 simplified()->LoadElement(
6186 elements, jsgraph()->ZeroConstant(), etrue1, if_true1);
6187
6188 // Ensure that we aren't shifting a copy-on-write backing store.
6190 elements = etrue1 =
6191 graph()->NewNode(simplified()->EnsureWritableFastElements(),
6192 receiver, elements, etrue1, if_true1);
6193 }
6194
6195 // Shift the remaining {elements} by one towards the start.
6196 Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1);
6197 Node* eloop =
6198 graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop);
6199 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
6200 MergeControlToEnd(graph(), common(), terminate);
6201
6202 Node* index = graph()->NewNode(
6204 jsgraph()->OneConstant(),
6205 jsgraph()->ConstantNoHole(JSArray::kMaxCopyElements - 1), loop);
6206
6207 {
6208 Node* check2 =
6209 graph()->NewNode(simplified()->NumberLessThan(), index, length);
6210 Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop);
6211
6212 if_true1 = graph()->NewNode(common()->IfFalse(), branch2);
6213 etrue1 = eloop;
6214
6215 Node* control2 = graph()->NewNode(common()->IfTrue(), branch2);
6216 Node* effect2 = etrue1;
6217
6218 ElementAccess const access =
6220
6221 // When disable v8_flags.turbo_loop_variable, typer cannot infer index
6222 // is in [1, kMaxCopyElements-1], and will break in representing
6223 // kRepFloat64 (Range(1, inf)) to kRepWord64 when converting
6224 // input for kLoadElement. So we need to add type guard here.
6225 // And we need to use index when using NumberLessThan to check
6226 // terminate and updating index, otherwise which will break inducing
6227 // variables in LoopVariableOptimizer.
6228 static_assert(JSArray::kMaxCopyElements < kSmiMaxValue);
6229 Node* index_retyped = effect2 =
6230 graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()),
6231 index, effect2, control2);
6232
6233 Node* value2 = effect2 =
6234 graph()->NewNode(simplified()->LoadElement(access), elements,
6235 index_retyped, effect2, control2);
6236 effect2 = graph()->NewNode(
6237 simplified()->StoreElement(access), elements,
6238 graph()->NewNode(simplified()->NumberSubtract(), index_retyped,
6239 jsgraph()->OneConstant()),
6240 value2, effect2, control2);
6241
6242 loop->ReplaceInput(1, control2);
6243 eloop->ReplaceInput(1, effect2);
6244 index->ReplaceInput(1,
6245 graph()->NewNode(simplified()->NumberAdd(), index,
6246 jsgraph()->OneConstant()));
6247 }
6248
6249 // Compute the new {length}.
6250 Node* new_length = graph()->NewNode(simplified()->NumberSubtract(),
6251 length, jsgraph()->OneConstant());
6252
6253 if (v8_flags.turbo_typer_hardening) {
6254 new_length = etrue1 = graph()->NewNode(
6257 new_length, length, etrue1, if_true1);
6258 }
6259
6260 // Store the new {length} to the {receiver}.
6261 etrue1 = graph()->NewNode(
6263 receiver, new_length, etrue1, if_true1);
6264
6265 // Store a hole to the element we just removed from the {receiver}.
6266 etrue1 = graph()->NewNode(
6269 elements, new_length, jsgraph()->TheHoleConstant(), etrue1,
6270 if_true1);
6271 }
6272
6273 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
6274 Node* efalse1 = efalse0;
6275 Node* vfalse1;
6276 {
6277 // Call the generic C++ implementation.
6278 const Builtin builtin = Builtin::kArrayShift;
6279 auto call_descriptor = Linkage::GetCEntryStubCallDescriptor(
6281 Builtins::name(builtin), node->op()->properties(),
6283 const bool has_builtin_exit_frame = true;
6285 has_builtin_exit_frame);
6286 Address builtin_entry = Builtins::CppEntryOf(builtin);
6287 Node* entry = jsgraph()->ExternalConstant(
6288 ExternalReference::Create(builtin_entry));
6289 Node* argc = jsgraph()->ConstantNoHole(
6291 static_assert(BuiltinArguments::kNewTargetIndex == 0);
6292 static_assert(BuiltinArguments::kTargetIndex == 1);
6293 static_assert(BuiltinArguments::kArgcIndex == 2);
6294 static_assert(BuiltinArguments::kPaddingIndex == 3);
6295 if_false1 = efalse1 = vfalse1 =
6296 graph()->NewNode(common()->Call(call_descriptor), stub_code,
6297 receiver, jsgraph()->PaddingConstant(), argc,
6298 target, jsgraph()->UndefinedConstant(), entry,
6299 argc, context, frame_state, efalse1, if_false1);
6300 }
6301
6302 if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
6303 efalse0 =
6304 graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0);
6305 vfalse0 =
6307 vtrue1, vfalse1, if_false0);
6308 }
6309
6310 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
6311 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
6312 value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
6313 vtrue0, vfalse0, control);
6314
6315 // Convert the hole to undefined. Do this last, so that we can optimize
6316 // conversion operator via some smart strength reduction in many cases.
6318 value =
6319 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
6320 }
6321
6322 controls_to_merge.push_back(control);
6323 effects_to_merge.push_back(effect);
6324 values_to_merge.push_back(value);
6325 }
6326
6327 if (controls_to_merge.size() > 1) {
6328 int const count = static_cast<int>(controls_to_merge.size());
6329
6330 control = graph()->NewNode(common()->Merge(count), count,
6331 &controls_to_merge.front());
6332 effects_to_merge.push_back(control);
6333 effect = graph()->NewNode(common()->EffectPhi(count), count + 1,
6334 &effects_to_merge.front());
6335 values_to_merge.push_back(control);
6336 value =
6338 count + 1, &values_to_merge.front());
6339 }
6340
6341 ReplaceWithValue(node, value, effect, control);
6342 return Replace(value);
6343}
6344
6345// ES6 section 22.1.3.23 Array.prototype.slice ( )
6347 if (!v8_flags.turbo_inline_array_builtins) return NoChange();
6348 JSCallNode n(node);
6349 CallParameters const& p = n.Parameters();
6351 return NoChange();
6352 }
6353
6354 Node* receiver = n.receiver();
6355 Node* start = n.ArgumentOr(0, jsgraph()->ZeroConstant());
6356 Node* end = n.ArgumentOrUndefined(1, jsgraph());
6357 Node* context = n.context();
6358 Effect effect = n.effect();
6359 Control control = n.control();
6360
6361 // Optimize for the case where we simply clone the {receiver}, i.e. when the
6362 // {start} is zero and the {end} is undefined (meaning it will be set to
6363 // {receiver}s "length" property).
6364
6365 // This logic should be in sync with ArrayPrototypeSlice (to a reasonable
6366 // degree). This is because CloneFastJSArray produces arrays which are
6367 // potentially COW. If there's a discrepancy, TF generates code which produces
6368 // a COW array and then expects it to be non-COW (or the other way around) ->
6369 // immediate deopt.
6370
6371 // LINT.IfChange(ArrayPrototypeSlice)
6372 if (!NumberMatcher(start).Is(0) ||
6373 !HeapObjectMatcher(end).Is(factory()->undefined_value())) {
6374 return NoChange();
6375 }
6376 // LINT.ThenChange(/src/builtins/array-slice.tq:ArrayPrototypeSlice)
6377
6378 MapInference inference(broker(), receiver, effect);
6379 if (!inference.HaveMaps()) return NoChange();
6380 ZoneRefSet<Map> const& receiver_maps = inference.GetMaps();
6381
6382 // Check that the maps are of JSArray (and more).
6383 // TODO(turbofan): Consider adding special case for the common pattern
6384 // `slice.call(arguments)`, for example jQuery makes heavy use of that.
6385 bool can_be_holey = false;
6386 for (MapRef receiver_map : receiver_maps) {
6387 if (!receiver_map.supports_fast_array_iteration(broker())) {
6388 return inference.NoChange();
6389 }
6390 if (IsHoleyElementsKind(receiver_map.elements_kind())) {
6391 can_be_holey = true;
6392 }
6393 }
6394
6395 if (!dependencies()->DependOnArraySpeciesProtector()) {
6396 return inference.NoChange();
6397 }
6398 if (can_be_holey && !dependencies()->DependOnNoElementsProtector()) {
6399 return inference.NoChange();
6400 }
6401 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
6402 control, p.feedback());
6403
6404 // TODO(turbofan): We can do even better here, either adding a CloneArray
6405 // simplified operator, whose output type indicates that it's an Array,
6406 // saving subsequent checks, or yet better, by introducing new operators
6407 // CopySmiOrObjectElements / CopyDoubleElements and inlining the JSArray
6408 // allocation in here. That way we'd even get escape analysis and scalar
6409 // replacement to help in some cases.
6410 Callable callable =
6411 Builtins::CallableFor(isolate(), Builtin::kCloneFastJSArray);
6412 auto call_descriptor = Linkage::GetStubCallDescriptor(
6413 graph()->zone(), callable.descriptor(),
6416
6417 // Calls to Builtin::kCloneFastJSArray produce COW arrays
6418 // if the original array is COW
6419 Node* clone = effect =
6420 graph()->NewNode(common()->Call(call_descriptor),
6421 jsgraph()->HeapConstantNoHole(callable.code()), receiver,
6422 context, effect, control);
6423
6424 ReplaceWithValue(node, clone, effect, control);
6425 return Replace(clone);
6426}
6427
6428// ES6 section 22.1.2.2 Array.isArray ( arg )
6430 // We certainly know that undefined is not an array.
6431 JSCallNode n(node);
6432 if (n.ArgumentCount() < 1) {
6433 Node* value = jsgraph()->FalseConstant();
6434 ReplaceWithValue(node, value);
6435 return Replace(value);
6436 }
6437
6438 Effect effect = n.effect();
6439 Control control = n.control();
6440 Node* context = n.context();
6441 FrameState frame_state = n.frame_state();
6442 Node* object = n.Argument(0);
6443 node->ReplaceInput(0, object);
6444 node->ReplaceInput(1, context);
6445 node->ReplaceInput(2, frame_state);
6446 node->ReplaceInput(3, effect);
6447 node->ReplaceInput(4, control);
6448 node->TrimInputCount(5);
6449 NodeProperties::ChangeOp(node, javascript()->ObjectIsArray());
6450 return Changed(node);
6451}
6452
6454 ArrayIteratorKind array_kind,
6455 IterationKind iteration_kind) {
6456 JSCallNode n(node);
6457 Node* receiver = n.receiver();
6458 Node* context = n.context();
6459 Effect effect = n.effect();
6460 Control control = n.control();
6461
6462 // Check if we know that {receiver} is a valid JSReceiver.
6463 MapInference inference(broker(), receiver, effect);
6464 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAreJSReceiver()) {
6465 return NoChange();
6466 }
6467
6468 // TypedArray iteration is stricter: it throws if the receiver is not a typed
6469 // array. So don't bother optimizing in that case.
6470 if (array_kind == ArrayIteratorKind::kTypedArray &&
6471 !inference.AllOfInstanceTypesAre(InstanceType::JS_TYPED_ARRAY_TYPE)) {
6472 return NoChange();
6473 }
6474
6475 if (array_kind == ArrayIteratorKind::kTypedArray) {
6476 // Make sure we deopt when the JSArrayBuffer is detached.
6477 if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
6478 CallParameters const& p = CallParametersOf(node->op());
6480 return NoChange();
6481 }
6482
6483 std::set<ElementsKind> elements_kinds;
6484 for (MapRef map : inference.GetMaps()) {
6485 elements_kinds.insert(map.elements_kind());
6486 }
6487
6488 // The following detach check is built with the given {elements_kinds} in
6489 // mind, so we have to install dependency on those.
6490 inference.RelyOnMapsPreferStability(
6491 dependencies(), jsgraph(), &effect, control,
6492 CallParametersOf(node->op()).feedback());
6493
6494 JSCallReducerAssembler a(this, node);
6495 a.CheckIfTypedArrayWasDetachedOrOutOfBounds(
6497 std::move(elements_kinds), p.feedback());
6498 std::tie(effect, control) = ReleaseEffectAndControlFromAssembler(&a);
6499 }
6500 }
6501
6502 // JSCreateArrayIterator doesn't have control output, so we bypass the old
6503 // JSCall node on the control chain.
6504 ReplaceWithValue(node, node, node, control);
6505
6506 // Morph the {node} into a JSCreateArrayIterator with the given {kind}.
6507 node->ReplaceInput(0, receiver);
6508 node->ReplaceInput(1, context);
6509 node->ReplaceInput(2, effect);
6510 node->ReplaceInput(3, control);
6511 node->TrimInputCount(4);
6513 javascript()->CreateArrayIterator(iteration_kind));
6514 return Changed(node);
6515}
6516
6517// ES #sec-%arrayiteratorprototype%.next
6519 JSCallNode n(node);
6520 CallParameters const& p = n.Parameters();
6521 Node* iterator = n.receiver();
6522 Node* context = n.context();
6523 Effect effect = n.effect();
6524 Control control = n.control();
6525
6527 return NoChange();
6528 }
6529
6530 if (iterator->opcode() != IrOpcode::kJSCreateArrayIterator) return NoChange();
6531
6532 IterationKind const iteration_kind =
6533 CreateArrayIteratorParametersOf(iterator->op()).kind();
6534 Node* iterated_object = NodeProperties::GetValueInput(iterator, 0);
6535 Effect iterator_effect{NodeProperties::GetEffectInput(iterator)};
6536
6537 MapInference inference(broker(), iterated_object, iterator_effect);
6538 if (!inference.HaveMaps()) return NoChange();
6539 ZoneRefSet<Map> const& iterated_object_maps = inference.GetMaps();
6540
6541 // Check that various {iterated_object_maps} have compatible elements kinds.
6542 ElementsKind elements_kind = iterated_object_maps[0].elements_kind();
6543 if (IsTypedArrayElementsKind(elements_kind)) {
6544 // TurboFan doesn't support loading from BigInt typed arrays yet.
6545 if (elements_kind == BIGUINT64_ELEMENTS ||
6546 elements_kind == BIGINT64_ELEMENTS) {
6547 return inference.NoChange();
6548 }
6549 for (MapRef iterated_object_map : iterated_object_maps) {
6550 if (iterated_object_map.elements_kind() != elements_kind) {
6551 return inference.NoChange();
6552 }
6553 }
6554 } else {
6555 if (!CanInlineArrayIteratingBuiltin(broker(), iterated_object_maps,
6556 &elements_kind)) {
6557 return inference.NoChange();
6558 }
6559 }
6560
6561 if (IsHoleyElementsKind(elements_kind) &&
6562 !dependencies()->DependOnNoElementsProtector()) {
6563 return inference.NoChange();
6564 }
6565
6566 if (IsFloat16TypedArrayElementsKind(elements_kind)) {
6567 // TODO(v8:14212): Allow optimizing Float16 typed arrays here, once they are
6568 // supported in the rest of the compiler.
6569 return inference.NoChange();
6570 }
6571
6572 // Since the map inference was done relative to {iterator_effect} rather than
6573 // {effect}, we need to guard the use of the map(s) even when the inference
6574 // was reliable.
6575 inference.InsertMapChecks(jsgraph(), &effect, control, p.feedback());
6576
6577 if (IsTypedArrayElementsKind(elements_kind)) {
6578 // See if we can skip the detaching check.
6579 if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
6580 // Bail out if the {iterated_object}s JSArrayBuffer was detached.
6581 Node* buffer = effect = graph()->NewNode(
6583 iterated_object, effect, control);
6584 Node* buffer_bit_field = effect = graph()->NewNode(
6586 buffer, effect, control);
6587 Node* check = graph()->NewNode(
6588 simplified()->NumberEqual(),
6589 graph()->NewNode(
6590 simplified()->NumberBitwiseAnd(), buffer_bit_field,
6591 jsgraph()->ConstantNoHole(JSArrayBuffer::WasDetachedBit::kMask)),
6592 jsgraph()->ZeroConstant());
6593 effect = graph()->NewNode(
6594 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached,
6595 p.feedback()),
6596 check, effect, control);
6597 }
6598 }
6599
6600 // Load the [[NextIndex]] from the {iterator} and leverage the fact
6601 // that we definitely know that it's in Unsigned32 range since the
6602 // {iterated_object} is either a JSArray or a JSTypedArray. For the
6603 // latter case we even know that it's a Smi in UnsignedSmall range.
6605 if (IsTypedArrayElementsKind(elements_kind)) {
6607 } else {
6608 index_access.type = TypeCache::Get()->kJSArrayLengthType;
6609 }
6610 Node* index = effect = graph()->NewNode(simplified()->LoadField(index_access),
6611 iterator, effect, control);
6612
6613 // Load the elements of the {iterated_object}. While it feels
6614 // counter-intuitive to place the elements pointer load before
6615 // the condition below, as it might not be needed (if the {index}
6616 // is out of bounds for the {iterated_object}), it's better this
6617 // way as it allows the LoadElimination to eliminate redundant
6618 // reloads of the elements pointer.
6619 Node* elements = effect = graph()->NewNode(
6621 iterated_object, effect, control);
6622
6623 // Load the length of the {iterated_object}. Due to the map checks we
6624 // already know something about the length here, which we can leverage
6625 // to generate Word32 operations below without additional checking.
6626 FieldAccess length_access =
6627 IsTypedArrayElementsKind(elements_kind)
6629 : AccessBuilder::ForJSArrayLength(elements_kind);
6630 Node* length = effect = graph()->NewNode(
6631 simplified()->LoadField(length_access), iterated_object, effect, control);
6632
6633 // Check whether {index} is within the valid range for the {iterated_object}.
6634 Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length);
6635 Node* branch =
6636 graph()->NewNode(common()->Branch(BranchHint::kNone), check, control);
6637
6638 Node* done_true;
6639 Node* value_true;
6640 Node* etrue = effect;
6641 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
6642 {
6643 // This extra check exists to refine the type of {index} but also to break
6644 // an exploitation technique that abuses typer mismatches.
6645 if (v8_flags.turbo_typer_hardening) {
6646 index = etrue = graph()->NewNode(
6649 index, length, etrue, if_true);
6650 }
6651
6652 done_true = jsgraph()->FalseConstant();
6653 if (iteration_kind == IterationKind::kKeys) {
6654 // Just return the {index}.
6655 value_true = index;
6656 } else {
6657 DCHECK(iteration_kind == IterationKind::kEntries ||
6658 iteration_kind == IterationKind::kValues);
6659
6660 if (IsTypedArrayElementsKind(elements_kind)) {
6661 Node* base_ptr = etrue =
6662 graph()->NewNode(simplified()->LoadField(
6664 iterated_object, etrue, if_true);
6665 Node* external_ptr = etrue = graph()->NewNode(
6666 simplified()->LoadField(
6668 iterated_object, etrue, if_true);
6669
6671 switch (elements_kind) {
6672#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
6673 case TYPE##_ELEMENTS: \
6674 array_type = kExternal##Type##Array; \
6675 break;
6677 default:
6678 UNREACHABLE();
6679#undef TYPED_ARRAY_CASE
6680 }
6681
6682 Node* buffer = etrue =
6683 graph()->NewNode(simplified()->LoadField(
6685 iterated_object, etrue, if_true);
6686
6687 value_true = etrue =
6688 graph()->NewNode(simplified()->LoadTypedElement(array_type), buffer,
6689 base_ptr, external_ptr, index, etrue, if_true);
6690 } else {
6691 value_true = etrue = graph()->NewNode(
6692 simplified()->LoadElement(
6694 elements, index, etrue, if_true);
6695
6696 // Convert hole to undefined if needed.
6697 if (IsHoleyElementsKind(elements_kind)) {
6698 value_true = ConvertHoleToUndefined(value_true, elements_kind);
6699 }
6700 }
6701
6702 if (iteration_kind == IterationKind::kEntries) {
6703 // Allocate elements for key/value pair
6704 value_true = etrue =
6705 graph()->NewNode(javascript()->CreateKeyValueArray(), index,
6706 value_true, context, etrue);
6707 } else {
6708 DCHECK_EQ(IterationKind::kValues, iteration_kind);
6709 }
6710 }
6711
6712 // Increment the [[NextIndex]] field in the {iterator}. The TypeGuards
6713 // above guarantee that the {next_index} is in the UnsignedSmall range.
6714 Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index,
6715 jsgraph()->OneConstant());
6716 etrue = graph()->NewNode(simplified()->StoreField(index_access), iterator,
6717 next_index, etrue, if_true);
6718 }
6719
6720 Node* done_false;
6721 Node* value_false;
6722 Node* efalse = effect;
6723 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
6724 {
6725 // iterator.[[NextIndex]] >= array.length, stop iterating.
6726 done_false = jsgraph()->TrueConstant();
6727 value_false = jsgraph()->UndefinedConstant();
6728
6729 if (!IsTypedArrayElementsKind(elements_kind)) {
6730 // Mark the {iterator} as exhausted by setting the [[NextIndex]] to a
6731 // value that will never pass the length check again (aka the maximum
6732 // value possible for the specific iterated object). Note that this is
6733 // different from what the specification says, which is changing the
6734 // [[IteratedObject]] field to undefined, but that makes it difficult
6735 // to eliminate the map checks and "length" accesses in for..of loops.
6736 //
6737 // This is not necessary for JSTypedArray's, since the length of those
6738 // cannot change later and so if we were ever out of bounds for them
6739 // we will stay out-of-bounds forever.
6740 Node* end_index = jsgraph()->ConstantNoHole(index_access.type.Max());
6741 efalse = graph()->NewNode(simplified()->StoreField(index_access),
6742 iterator, end_index, efalse, if_false);
6743 }
6744 }
6745
6746 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
6747 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
6748 Node* value =
6750 value_true, value_false, control);
6751 Node* done =
6753 done_true, done_false, control);
6754
6755 // Create IteratorResult object.
6756 value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
6757 value, done, context, effect);
6758 ReplaceWithValue(node, value, effect, control);
6759 return Replace(value);
6760}
6761
6762// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
6763// ES6 section 21.1.3.3 String.prototype.codePointAt ( pos )
6765 const Operator* string_access_operator, Node* node) {
6766 DCHECK(string_access_operator->opcode() == IrOpcode::kStringCharCodeAt ||
6767 string_access_operator->opcode() == IrOpcode::kStringCodePointAt);
6768 JSCallNode n(node);
6769 CallParameters const& p = n.Parameters();
6771 return NoChange();
6772 }
6773
6774 Node* receiver = n.receiver();
6775 Node* index = n.ArgumentOr(0, jsgraph()->ZeroConstant());
6776 Effect effect = n.effect();
6777 Control control = n.control();
6778
6779 // Ensure that the {receiver} is actually a String.
6780 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
6781 receiver, effect, control);
6782
6783 // Determine the {receiver} length.
6784 Node* receiver_length =
6785 graph()->NewNode(simplified()->StringLength(), receiver);
6786
6787 // Check that the {index} is within range.
6788 index = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
6789 index, receiver_length, effect, control);
6790
6791 // Return the character from the {receiver} as single character string.
6792 Node* value = effect = graph()->NewNode(string_access_operator, receiver,
6793 index, effect, control);
6794
6795 ReplaceWithValue(node, value, effect, control);
6796 return Replace(value);
6797}
6798
6799// ES section 21.1.3.20
6800// String.prototype.startsWith ( searchString [ , position ] )
6802 JSCallNode n(node);
6803 CallParameters const& p = n.Parameters();
6805 return NoChange();
6806 }
6807
6808 TNode<Object> search_element = n.ArgumentOrUndefined(0, jsgraph());
6809
6810 // Here are three conditions:
6811 // First, If search_element is definitely not a string, we make no change.
6812 // Second, If search_element is definitely a string and its length is less
6813 // or equal than max inline matching sequence threshold, we could inline
6814 // the entire matching sequence.
6815 // Third, we try to inline, and have a runtime deopt if search_element is
6816 // not a string.
6817 HeapObjectMatcher search_element_matcher(search_element);
6818 if (search_element_matcher.HasResolvedValue()) {
6819 ObjectRef target_ref = search_element_matcher.Ref(broker());
6820 if (!target_ref.IsString()) return NoChange();
6821 StringRef search_element_string = target_ref.AsString();
6822 if (!search_element_string.IsContentAccessible()) return NoChange();
6823 int length = search_element_string.length();
6824 // If search_element's length is less or equal than
6825 // kMaxInlineMatchSequence, we inline the entire
6826 // matching sequence.
6827 if (length <= kMaxInlineMatchSequence) {
6828 JSCallReducerAssembler a(this, node);
6829 Node* subgraph = a.ReduceStringPrototypeStartsWith(search_element_string);
6830 return ReplaceWithSubgraph(&a, subgraph);
6831 }
6832 }
6833
6834 JSCallReducerAssembler a(this, node);
6835 Node* subgraph = a.ReduceStringPrototypeStartsWith();
6836 return ReplaceWithSubgraph(&a, subgraph);
6837}
6838
6839// ES section 21.1.3.7
6840// String.prototype.endsWith(searchString [, endPosition])
6842 JSCallNode n(node);
6843 CallParameters const& p = n.Parameters();
6845 return NoChange();
6846 }
6847
6848 TNode<Object> search_element = n.ArgumentOrUndefined(0, jsgraph());
6849
6850 // Here are three conditions:
6851 // First, If search_element is definitely not a string, we make no change.
6852 // Second, If search_element is definitely a string and its length is less
6853 // or equal than max inline matching sequence threshold, we could inline
6854 // the entire matching sequence.
6855 // Third, we try to inline, and have a runtime deopt if search_element is
6856 // not a string.
6857 HeapObjectMatcher search_element_matcher(search_element);
6858 if (search_element_matcher.HasResolvedValue()) {
6859 ObjectRef target_ref = search_element_matcher.Ref(broker());
6860 if (!target_ref.IsString()) return NoChange();
6861 StringRef search_element_string = target_ref.AsString();
6862 if (!search_element_string.IsContentAccessible()) return NoChange();
6863 int length = search_element_string.length();
6864 // If search_element's length is less or equal than
6865 // kMaxInlineMatchSequence, we inline the entire
6866 // matching sequence.
6867 if (length <= kMaxInlineMatchSequence) {
6868 JSCallReducerAssembler a(this, node);
6869 Node* subgraph = a.ReduceStringPrototypeEndsWith(search_element_string);
6870 return ReplaceWithSubgraph(&a, subgraph);
6871 }
6872 }
6873
6874 JSCallReducerAssembler a(this, node);
6875 Node* subgraph = a.ReduceStringPrototypeEndsWith();
6876 return ReplaceWithSubgraph(&a, subgraph);
6877}
6878
6879// ES section 21.1.3.1 String.prototype.charAt ( pos )
6881 JSCallNode n(node);
6882 CallParameters const& p = n.Parameters();
6884 return NoChange();
6885 }
6886
6887 Node* receiver = n.receiver();
6888 Node* index = n.ArgumentOr(0, jsgraph()->ZeroConstant());
6889
6890 // Attempt to constant fold the operation when we know
6891 // that receiver is a string and index is a number.
6892 HeapObjectMatcher receiver_matcher(receiver);
6893 NumberMatcher index_matcher(index);
6894 if (receiver_matcher.HasResolvedValue()) {
6895 HeapObjectRef receiver_ref = receiver_matcher.Ref(broker());
6896 if (!receiver_ref.IsString()) return NoChange();
6897 StringRef receiver_string = receiver_ref.AsString();
6898 bool is_content_accessible = receiver_string.IsContentAccessible();
6899 bool is_integer_in_max_range =
6900 index_matcher.IsInteger() &&
6901 index_matcher.IsInRange(
6902 0.0, static_cast<double>(JSObject::kMaxElementIndex));
6903 if (is_content_accessible && is_integer_in_max_range) {
6904 JSCallReducerAssembler a(this, node);
6905 Node* subgraph = a.ReduceStringPrototypeCharAt(
6906 receiver_string,
6907 static_cast<uint32_t>(index_matcher.ResolvedValue()));
6908 return ReplaceWithSubgraph(&a, subgraph);
6909 }
6910 }
6911
6912 JSCallReducerAssembler a(this, node);
6913 Node* subgraph = a.ReduceStringPrototypeCharAt();
6914 return ReplaceWithSubgraph(&a, subgraph);
6915}
6916
6917#ifdef V8_INTL_SUPPORT
6918
6919Reduction JSCallReducer::ReduceStringPrototypeToLowerCaseIntl(Node* node) {
6920 JSCallNode n(node);
6921 CallParameters const& p = n.Parameters();
6923 return NoChange();
6924 }
6925 Effect effect = n.effect();
6926 Control control = n.control();
6927
6928 Node* receiver = effect = graph()->NewNode(
6929 simplified()->CheckString(p.feedback()), n.receiver(), effect, control);
6930
6933 node->ReplaceInput(0, receiver);
6934 node->TrimInputCount(1);
6935 NodeProperties::ChangeOp(node, simplified()->StringToLowerCaseIntl());
6936 NodeProperties::SetType(node, Type::String());
6937 return Changed(node);
6938}
6939
6940Reduction JSCallReducer::ReduceStringPrototypeToUpperCaseIntl(Node* node) {
6941 JSCallNode n(node);
6942 CallParameters const& p = n.Parameters();
6943 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
6944 return NoChange();
6945 }
6946 Effect effect = n.effect();
6947 Control control = n.control();
6948
6949 Node* receiver = effect = graph()->NewNode(
6950 simplified()->CheckString(p.feedback()), n.receiver(), effect, control);
6951
6954 node->ReplaceInput(0, receiver);
6955 node->TrimInputCount(1);
6956 NodeProperties::ChangeOp(node, simplified()->StringToUpperCaseIntl());
6957 NodeProperties::SetType(node, Type::String());
6958 return Changed(node);
6959}
6960
6961#endif // V8_INTL_SUPPORT
6962
6963// ES #sec-string.fromcharcode
6965 JSCallNode n(node);
6966 CallParameters const& p = n.Parameters();
6968 return NoChange();
6969 }
6970 if (n.ArgumentCount() == 1) {
6971 Effect effect = n.effect();
6972 Control control = n.control();
6973 Node* input = n.Argument(0);
6974
6975 input = effect = graph()->NewNode(
6977 p.feedback()),
6978 input, effect, control);
6979
6980 Node* value =
6981 graph()->NewNode(simplified()->StringFromSingleCharCode(), input);
6982 ReplaceWithValue(node, value, effect);
6983 return Replace(value);
6984 }
6985 return NoChange();
6986}
6987
6988// ES #sec-string.fromcodepoint
6990 JSCallNode n(node);
6991 CallParameters const& p = n.Parameters();
6993 return NoChange();
6994 }
6995 if (n.ArgumentCount() != 1) return NoChange();
6996
6997 Effect effect = n.effect();
6998 Control control = n.control();
6999 Node* input = n.Argument(0);
7000
7001 input = effect = graph()->NewNode(
7004 input, jsgraph()->ConstantNoHole(0x10FFFF + 1), effect, control);
7005
7006 Node* value =
7007 graph()->NewNode(simplified()->StringFromSingleCodePoint(), input);
7008 ReplaceWithValue(node, value, effect);
7009 return Replace(value);
7010}
7011
7013 JSCallNode n(node);
7014 CallParameters const& p = n.Parameters();
7016 return NoChange();
7017 }
7018 Node* effect = NodeProperties::GetEffectInput(node);
7019 Node* control = NodeProperties::GetControlInput(node);
7020 Node* receiver = effect = graph()->NewNode(
7021 simplified()->CheckString(p.feedback()), n.receiver(), effect, control);
7022 Node* iterator = effect =
7023 graph()->NewNode(javascript()->CreateStringIterator(), receiver,
7024 jsgraph()->NoContextConstant(), effect);
7025 ReplaceWithValue(node, iterator, effect, control);
7026 return Replace(iterator);
7027}
7028
7029#ifdef V8_INTL_SUPPORT
7030
7031Reduction JSCallReducer::ReduceStringPrototypeLocaleCompareIntl(Node* node) {
7032 JSCallNode n(node);
7033 // Signature: receiver.localeCompare(compareString, locales, options)
7034 if (n.ArgumentCount() < 1 || n.ArgumentCount() > 3) {
7035 return NoChange();
7036 }
7037
7038 {
7039 DirectHandle<Object> locales;
7040 {
7041 HeapObjectMatcher m(n.ArgumentOrUndefined(1, jsgraph()));
7042 if (!m.HasResolvedValue()) return NoChange();
7043 if (m.Is(factory()->undefined_value())) {
7044 locales = factory()->undefined_value();
7045 } else {
7046 ObjectRef ref = m.Ref(broker());
7047 if (!ref.IsString()) return NoChange();
7048 StringRef sref = ref.AsString();
7049 if (std::optional<Handle<String>> maybe_locales =
7050 sref.ObjectIfContentAccessible(broker())) {
7051 locales = *maybe_locales;
7052 } else {
7053 return NoChange();
7054 }
7055 }
7056 }
7057
7058 TNode<Object> options = n.ArgumentOrUndefined(2, jsgraph());
7059 {
7060 HeapObjectMatcher m(options);
7061 if (!m.Is(factory()->undefined_value())) {
7062 return NoChange();
7063 }
7064 }
7065
7066 if (Intl::CompareStringsOptionsFor(broker()->local_isolate_or_isolate(),
7067 locales, factory()->undefined_value()) !=
7069 return NoChange();
7070 }
7071 }
7072
7073 Callable callable =
7074 Builtins::CallableFor(isolate(), Builtin::kStringFastLocaleCompare);
7075 auto call_descriptor = Linkage::GetStubCallDescriptor(
7076 graph()->zone(), callable.descriptor(),
7077 callable.descriptor().GetStackParameterCount(),
7079 node->RemoveInput(n.FeedbackVectorIndex());
7080 if (n.ArgumentCount() == 3) {
7081 node->RemoveInput(n.ArgumentIndex(2));
7082 } else if (n.ArgumentCount() == 1) {
7083 node->InsertInput(graph()->zone(), n.LastArgumentIndex() + 1,
7085 } else {
7086 DCHECK_EQ(2, n.ArgumentCount());
7087 }
7088 node->InsertInput(graph()->zone(), 0,
7089 jsgraph()->HeapConstantNoHole(callable.code()));
7090 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
7091 return Changed(node);
7092}
7093#endif // V8_INTL_SUPPORT
7094
7096 JSCallNode n(node);
7097 Node* receiver = n.receiver();
7098 Effect effect = n.effect();
7099 Control control = n.control();
7100 Node* context = n.context();
7101
7102 MapInference inference(broker(), receiver, effect);
7103 if (!inference.HaveMaps() ||
7104 !inference.AllOfInstanceTypesAre(JS_STRING_ITERATOR_TYPE)) {
7105 return NoChange();
7106 }
7107
7108 Node* string = effect = graph()->NewNode(
7110 receiver, effect, control);
7111 Node* index = effect = graph()->NewNode(
7113 receiver, effect, control);
7114 Node* length = graph()->NewNode(simplified()->StringLength(), string);
7115
7116 // branch0: if (index < length)
7117 Node* check0 =
7118 graph()->NewNode(simplified()->NumberLessThan(), index, length);
7119 Node* branch0 =
7120 graph()->NewNode(common()->Branch(BranchHint::kNone), check0, control);
7121
7122 Node* etrue0 = effect;
7123 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
7124 Node* done_true;
7125 Node* vtrue0;
7126 {
7127 done_true = jsgraph()->FalseConstant();
7128 vtrue0 = etrue0 = graph()->NewNode(simplified()->StringFromCodePointAt(),
7129 string, index, etrue0, if_true0);
7130
7131 // Update iterator.[[NextIndex]]
7132 Node* char_length = graph()->NewNode(simplified()->StringLength(), vtrue0);
7133 index = graph()->NewNode(simplified()->NumberAdd(), index, char_length);
7134 etrue0 = graph()->NewNode(
7136 receiver, index, etrue0, if_true0);
7137 }
7138
7139 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
7140 Node* done_false;
7141 Node* vfalse0;
7142 {
7143 vfalse0 = jsgraph()->UndefinedConstant();
7144 done_false = jsgraph()->TrueConstant();
7145 }
7146
7147 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
7148 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, effect, control);
7149 Node* value =
7150 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0,
7151 vfalse0, control);
7152 Node* done =
7154 done_true, done_false, control);
7155
7156 value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
7157 value, done, context, effect);
7158
7159 ReplaceWithValue(node, value, effect, control);
7160 return Replace(value);
7161}
7162
7163// ES #sec-string.prototype.concat
7165 JSCallNode n(node);
7166 CallParameters const& p = n.Parameters();
7167 const int parameter_count = n.ArgumentCount();
7168 if (parameter_count > 1) return NoChange();
7170 return NoChange();
7171 }
7172
7173 Effect effect = n.effect();
7174 Control control = n.control();
7175 Node* receiver = effect = graph()->NewNode(
7176 simplified()->CheckString(p.feedback()), n.receiver(), effect, control);
7177
7178 if (parameter_count == 0) {
7179 ReplaceWithValue(node, receiver, effect, control);
7180 return Replace(receiver);
7181 }
7182
7183 Node* argument = effect = graph()->NewNode(
7184 simplified()->CheckString(p.feedback()), n.Argument(0), effect, control);
7185 Node* receiver_length =
7186 graph()->NewNode(simplified()->StringLength(), receiver);
7187 Node* argument_length =
7188 graph()->NewNode(simplified()->StringLength(), argument);
7189 Node* length = graph()->NewNode(simplified()->NumberAdd(), receiver_length,
7190 argument_length);
7191 length = effect = graph()->NewNode(
7192 simplified()->CheckBounds(p.feedback()), length,
7193 jsgraph()->ConstantNoHole(String::kMaxLength + 1), effect, control);
7194
7195 Node* value = graph()->NewNode(simplified()->StringConcat(), length, receiver,
7196 argument);
7197
7198 ReplaceWithValue(node, value, effect, control);
7199 return Replace(value);
7200}
7201
7202namespace {
7203
7204FrameState CreateStringCreateLazyDeoptContinuationFrameState(
7205 JSGraph* graph, SharedFunctionInfoRef shared, Node* target, Node* context,
7207 Node* const receiver = graph->TheHoleConstant();
7208 Node* stack_parameters[]{receiver};
7209 const int stack_parameter_count = arraysize(stack_parameters);
7211 graph, shared, Builtin::kStringCreateLazyDeoptContinuation, target,
7212 context, stack_parameters, stack_parameter_count, outer_frame_state,
7214}
7215
7216} // namespace
7217
7219 JSFunctionRef constructor) {
7220 JSConstructNode n(node);
7221 if (n.target() != n.new_target()) return NoChange();
7222
7223 DCHECK_EQ(constructor, native_context().string_function(broker_));
7224 DCHECK(constructor.initial_map(broker_).IsJSPrimitiveWrapperMap());
7225
7226 Node* context = n.context();
7227 FrameState frame_state = n.frame_state();
7228 Effect effect = n.effect();
7229 Control control = n.control();
7230
7231 Node* primitive_value;
7232 if (n.ArgumentCount() == 0) {
7233 primitive_value = jsgraph()->EmptyStringConstant();
7234 } else {
7235 // Insert a construct stub frame into the chain of frame states. This will
7236 // reconstruct the proper frame when deoptimizing within the constructor.
7237 frame_state = CreateConstructInvokeStubFrameState(
7238 node, frame_state, constructor.shared(broker_), context, common(),
7239 graph());
7240
7241 // This continuation takes the newly created primitive string, and wraps it
7242 // in a string wrapper, matching CreateStringWrapper.
7243 Node* continuation_frame_state =
7244 CreateStringCreateLazyDeoptContinuationFrameState(
7245 jsgraph(), constructor.shared(broker_), n.target(), context,
7246 frame_state);
7247
7248 primitive_value = effect = control =
7249 graph()->NewNode(javascript()->ToString(), n.Argument(0), context,
7250 continuation_frame_state, effect, control);
7251
7252 // Rewire potential exception edges.
7253 Node* on_exception = nullptr;
7254 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
7255 // Create appropriate {IfException} and {IfSuccess} nodes.
7256 Node* if_exception =
7257 graph()->NewNode(common()->IfException(), effect, control);
7258 control = graph()->NewNode(common()->IfSuccess(), control);
7259
7260 // Join the exception edges.
7261 Node* merge =
7262 graph()->NewNode(common()->Merge(2), if_exception, on_exception);
7263 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception,
7264 on_exception, merge);
7265 Node* phi =
7267 if_exception, on_exception, merge);
7268 ReplaceWithValue(on_exception, phi, ephi, merge);
7269 merge->ReplaceInput(1, on_exception);
7270 ephi->ReplaceInput(1, on_exception);
7271 phi->ReplaceInput(1, on_exception);
7272 }
7273 }
7274
7275 RelaxControls(node, control);
7276 node->ReplaceInput(0, primitive_value);
7277 node->ReplaceInput(1, context);
7278 node->ReplaceInput(2, effect);
7279 node->TrimInputCount(3);
7280 NodeProperties::ChangeOp(node, javascript()->CreateStringWrapper());
7281 return Changed(node);
7282}
7283
7286
7287 // We only inline when we have the executor.
7288 if (a.ConstructArity() < 1) return NoChange();
7289 // Only handle builtins Promises, not subclasses.
7290 if (a.TargetInput() != a.NewTargetInput()) return NoChange();
7291 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange();
7292
7293 TNode<Object> subgraph = a.ReducePromiseConstructor(native_context());
7294 return ReplaceWithSubgraph(&a, subgraph);
7295}
7296
7298 if (!inference->HaveMaps()) return false;
7299 ZoneRefSet<Map> const& receiver_maps = inference->GetMaps();
7300
7301 // Check whether all {receiver_maps} are JSPromise maps and
7302 // have the initial Promise.prototype as their [[Prototype]].
7303 for (MapRef receiver_map : receiver_maps) {
7304 if (!receiver_map.IsJSPromiseMap()) return false;
7305 HeapObjectRef prototype = receiver_map.prototype(broker());
7306 if (!prototype.equals(native_context().promise_prototype(broker()))) {
7307 return false;
7308 }
7309 }
7310
7311 return true;
7312}
7313
7314// ES section #sec-promise.prototype.catch
7316 JSCallNode n(node);
7317 CallParameters const& p = n.Parameters();
7319 return NoChange();
7320 }
7321 int arity = p.arity_without_implicit_args();
7322 Node* receiver = n.receiver();
7323 Effect effect = n.effect();
7324 Control control = n.control();
7325
7326 MapInference inference(broker(), receiver, effect);
7327 if (!DoPromiseChecks(&inference)) return inference.NoChange();
7328
7329 if (!dependencies()->DependOnPromiseThenProtector()) {
7330 return inference.NoChange();
7331 }
7332 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
7333 control, p.feedback());
7334
7335 // Massage the {node} to call "then" instead by first removing all inputs
7336 // following the onRejected parameter, and then filling up the parameters
7337 // to two inputs from the left with undefined.
7338 Node* target = jsgraph()->ConstantNoHole(
7339 native_context().promise_then(broker()), broker());
7340 NodeProperties::ReplaceValueInput(node, target, 0);
7342 for (; arity > 1; --arity) node->RemoveInput(3);
7343 for (; arity < 2; ++arity) {
7344 node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant());
7345 }
7347 node, javascript()->Call(
7351 return Changed(node).FollowedBy(ReducePromisePrototypeThen(node));
7352}
7353
7355 SharedFunctionInfoRef shared, Node* context, Node* effect, Node* control) {
7356 DCHECK(shared.HasBuiltinId());
7357 Handle<FeedbackCell> feedback_cell =
7358 isolate()->factory()->many_closures_cell();
7359 Callable const callable =
7360 Builtins::CallableFor(isolate(), shared.builtin_id());
7361 CodeRef code = MakeRef(broker(), *callable.code());
7362 return graph()->NewNode(javascript()->CreateClosure(shared, code),
7363 jsgraph()->HeapConstantNoHole(feedback_cell), context,
7364 effect, control);
7365}
7366
7367// ES section #sec-promise.prototype.finally
7369 JSCallNode n(node);
7370 CallParameters const& p = n.Parameters();
7371 int arity = p.arity_without_implicit_args();
7372 Node* receiver = n.receiver();
7373 Node* on_finally = n.ArgumentOrUndefined(0, jsgraph());
7374 Effect effect = n.effect();
7375 Control control = n.control();
7377 return NoChange();
7378 }
7379
7380 MapInference inference(broker(), receiver, effect);
7381 if (!DoPromiseChecks(&inference)) return inference.NoChange();
7382 ZoneRefSet<Map> const& receiver_maps = inference.GetMaps();
7383
7384 if (!dependencies()->DependOnPromiseHookProtector()) {
7385 return inference.NoChange();
7386 }
7387 if (!dependencies()->DependOnPromiseThenProtector()) {
7388 return inference.NoChange();
7389 }
7390 if (!dependencies()->DependOnPromiseSpeciesProtector()) {
7391 return inference.NoChange();
7392 }
7393 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
7394 control, p.feedback());
7395
7396 // Check if {on_finally} is callable, and if so wrap it into appropriate
7397 // closures that perform the finalization.
7398 Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), on_finally);
7399 Node* branch =
7400 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
7401
7402 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
7403 Node* etrue = effect;
7404 Node* catch_true;
7405 Node* then_true;
7406 {
7407 Node* context = jsgraph()->ConstantNoHole(native_context(), broker());
7408 Node* constructor = jsgraph()->ConstantNoHole(
7409 native_context().promise_function(broker()), broker());
7410
7411 // Allocate shared context for the closures below.
7412 context = etrue = graph()->NewNode(
7413 javascript()->CreateFunctionContext(
7414 native_context().scope_info(broker()),
7418 context, etrue, if_true);
7419 etrue = graph()->NewNode(
7420 simplified()->StoreField(
7422 context, on_finally, etrue, if_true);
7423 etrue = graph()->NewNode(
7424 simplified()->StoreField(
7426 context, constructor, etrue, if_true);
7427
7428 // Allocate the closure for the reject case.
7429 SharedFunctionInfoRef promise_catch_finally =
7430 MakeRef(broker(), factory()->promise_catch_finally_shared_fun());
7431 catch_true = etrue = CreateClosureFromBuiltinSharedFunctionInfo(
7432 promise_catch_finally, context, etrue, if_true);
7433
7434 // Allocate the closure for the fulfill case.
7435 SharedFunctionInfoRef promise_then_finally =
7436 MakeRef(broker(), factory()->promise_then_finally_shared_fun());
7438 promise_then_finally, context, etrue, if_true);
7439 }
7440
7441 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
7442 Node* efalse = effect;
7443 Node* catch_false = on_finally;
7444 Node* then_false = on_finally;
7445
7446 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
7447 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
7448 Node* catch_finally =
7450 catch_true, catch_false, control);
7451 Node* then_finally =
7453 then_true, then_false, control);
7454
7455 // At this point we definitely know that {receiver} has one of the
7456 // {receiver_maps}, so insert a MapGuard as a hint for the lowering
7457 // of the call to "then" below.
7458 {
7459 effect = graph()->NewNode(simplified()->MapGuard(receiver_maps), receiver,
7460 effect, control);
7461 }
7462
7463 // Massage the {node} to call "then" instead by first removing all inputs
7464 // following the onFinally parameter, and then replacing the only parameter
7465 // input with the {on_finally} value.
7466 Node* target = jsgraph()->ConstantNoHole(
7467 native_context().promise_then(broker()), broker());
7468 NodeProperties::ReplaceValueInput(node, target, n.TargetIndex());
7471 for (; arity > 2; --arity) node->RemoveInput(2);
7472 for (; arity < 2; ++arity) {
7473 node->InsertInput(graph()->zone(), 2, then_finally);
7474 }
7475 node->ReplaceInput(2, then_finally);
7476 node->ReplaceInput(3, catch_finally);
7478 node, javascript()->Call(
7482 return Changed(node).FollowedBy(ReducePromisePrototypeThen(node));
7483}
7484
7486 JSCallNode n(node);
7487 CallParameters const& p = n.Parameters();
7489 return NoChange();
7490 }
7491
7492 Node* receiver = n.receiver();
7493 Node* on_fulfilled = n.ArgumentOrUndefined(0, jsgraph());
7494 Node* on_rejected = n.ArgumentOrUndefined(1, jsgraph());
7495 Node* context = n.context();
7496 Effect effect = n.effect();
7497 Control control = n.control();
7498 FrameState frame_state = n.frame_state();
7499
7500 MapInference inference(broker(), receiver, effect);
7501 if (!DoPromiseChecks(&inference)) return inference.NoChange();
7502
7503 if (!dependencies()->DependOnPromiseHookProtector()) {
7504 return inference.NoChange();
7505 }
7506 if (!dependencies()->DependOnPromiseSpeciesProtector()) {
7507 return inference.NoChange();
7508 }
7509 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
7510 control, p.feedback());
7511
7512 // Check that {on_fulfilled} is callable.
7513 on_fulfilled = graph()->NewNode(
7515 graph()->NewNode(simplified()->ObjectIsCallable(), on_fulfilled),
7516 on_fulfilled, jsgraph()->UndefinedConstant());
7517
7518 // Check that {on_rejected} is callable.
7519 on_rejected = graph()->NewNode(
7521 graph()->NewNode(simplified()->ObjectIsCallable(), on_rejected),
7522 on_rejected, jsgraph()->UndefinedConstant());
7523
7524 // Create the resulting JSPromise.
7525 Node* promise = effect =
7526 graph()->NewNode(javascript()->CreatePromise(), context, effect);
7527
7528 // Chain {result} onto {receiver}.
7529 promise = effect = graph()->NewNode(
7530 javascript()->PerformPromiseThen(), receiver, on_fulfilled, on_rejected,
7531 promise, context, frame_state, effect, control);
7532
7533 // At this point we know that {promise} is going to have the
7534 // initial Promise map, since even if {PerformPromiseThen}
7535 // above called into the host rejection tracker, the {promise}
7536 // doesn't escape to user JavaScript. So bake this information
7537 // into the graph such that subsequent passes can use the
7538 // information for further optimizations.
7539 MapRef promise_map =
7540 native_context().promise_function(broker()).initial_map(broker());
7541 effect =
7542 graph()->NewNode(simplified()->MapGuard(ZoneRefSet<Map>(promise_map)),
7543 promise, effect, control);
7544
7545 ReplaceWithValue(node, promise, effect, control);
7546 return Replace(promise);
7547}
7548
7549// ES section #sec-promise.resolve
7551 JSCallNode n(node);
7552 Node* receiver = n.receiver();
7553 Node* value = n.ArgumentOrUndefined(0, jsgraph());
7554 Node* context = n.context();
7555 Effect effect = n.effect();
7556 Control control = n.control();
7557 FrameState frame_state = n.frame_state();
7558
7559 // Only reduce when the receiver is guaranteed to be a JSReceiver.
7560 MapInference inference(broker(), receiver, effect);
7561 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAreJSReceiver()) {
7562 return NoChange();
7563 }
7564
7565 // Morph the {node} into a JSPromiseResolve operation.
7566 node->ReplaceInput(0, receiver);
7567 node->ReplaceInput(1, value);
7568 node->ReplaceInput(2, context);
7569 node->ReplaceInput(3, frame_state);
7570 node->ReplaceInput(4, effect);
7571 node->ReplaceInput(5, control);
7572 node->TrimInputCount(6);
7573 NodeProperties::ChangeOp(node, javascript()->PromiseResolve());
7574 return Changed(node);
7575}
7576
7577// ES #sec-typedarray-constructors
7579 Node* node, SharedFunctionInfoRef shared) {
7580 JSConstructNode n(node);
7581 Node* target = n.target();
7582 Node* arg0 = n.ArgumentOrUndefined(0, jsgraph());
7583 Node* arg1 = n.ArgumentOrUndefined(1, jsgraph());
7584 Node* arg2 = n.ArgumentOrUndefined(2, jsgraph());
7585 Node* new_target = n.new_target();
7586 Node* context = n.context();
7587 FrameState frame_state = n.frame_state();
7588 Effect effect = n.effect();
7589 Control control = n.control();
7590
7591 // Insert a construct stub frame into the chain of frame states. This will
7592 // reconstruct the proper frame when deoptimizing within the constructor.
7593 frame_state = CreateConstructInvokeStubFrameState(node, frame_state, shared,
7594 context, common(), graph());
7595
7596 // This continuation just returns the newly created JSTypedArray. We
7597 // pass the_hole as the receiver, just like the builtin construct stub
7598 // does in this case.
7599 Node* const receiver = jsgraph()->TheHoleConstant();
7600 Node* continuation_frame_state = CreateGenericLazyDeoptContinuationFrameState(
7601 jsgraph(), shared, target, context, receiver, frame_state);
7602
7603 Node* result = graph()->NewNode(javascript()->CreateTypedArray(), target,
7604 new_target, arg0, arg1, arg2, context,
7605 continuation_frame_state, effect, control);
7606 return Replace(result);
7607}
7608
7609// ES #sec-get-%typedarray%.prototype-@@tostringtag
7612 Node* effect = NodeProperties::GetEffectInput(node);
7613 Node* control = NodeProperties::GetControlInput(node);
7614
7615 NodeVector values(graph()->zone());
7616 NodeVector effects(graph()->zone());
7617 NodeVector controls(graph()->zone());
7618
7619 Node* smi_check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
7620 control = graph()->NewNode(common()->Branch(BranchHint::kFalse), smi_check,
7621 control);
7622
7623 values.push_back(jsgraph()->UndefinedConstant());
7624 effects.push_back(effect);
7625 controls.push_back(graph()->NewNode(common()->IfTrue(), control));
7626
7627 control = graph()->NewNode(common()->IfFalse(), control);
7628 Node* receiver_map = effect =
7629 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
7630 receiver, effect, control);
7631 Node* receiver_bit_field2 = effect = graph()->NewNode(
7632 simplified()->LoadField(AccessBuilder::ForMapBitField2()), receiver_map,
7633 effect, control);
7634 Node* receiver_elements_kind = graph()->NewNode(
7635 simplified()->NumberShiftRightLogical(),
7636 graph()->NewNode(
7637 simplified()->NumberBitwiseAnd(), receiver_bit_field2,
7638 jsgraph()->ConstantNoHole(Map::Bits2::ElementsKindBits::kMask)),
7639 jsgraph()->ConstantNoHole(Map::Bits2::ElementsKindBits::kShift));
7640
7641 // Offset the elements kind by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
7642 // so that the branch cascade below is turned into a simple table
7643 // switch by the ControlFlowOptimizer later.
7644 receiver_elements_kind = graph()->NewNode(
7645 simplified()->NumberSubtract(), receiver_elements_kind,
7647
7648 // To be converted into a switch by the ControlFlowOptimizer, the below
7649 // code requires that TYPED_ARRAYS and RAB_GSAB_TYPED_ARRAYS are consecutive.
7650 static_assert(LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1 ==
7652#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
7653 do { \
7654 Node* check = graph()->NewNode( \
7655 simplified()->NumberEqual(), receiver_elements_kind, \
7656 jsgraph()->ConstantNoHole(TYPE##_ELEMENTS - \
7657 FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); \
7658 control = graph()->NewNode(common()->Branch(), check, control); \
7659 values.push_back(jsgraph()->ConstantNoHole( \
7660 broker()->GetTypedArrayStringTag(TYPE##_ELEMENTS), broker())); \
7661 effects.push_back(effect); \
7662 controls.push_back(graph()->NewNode(common()->IfTrue(), control)); \
7663 control = graph()->NewNode(common()->IfFalse(), control); \
7664 } while (false);
7667#undef TYPED_ARRAY_CASE
7668
7669 values.push_back(jsgraph()->UndefinedConstant());
7670 effects.push_back(effect);
7671 controls.push_back(control);
7672
7673 int const count = static_cast<int>(controls.size());
7674 control = graph()->NewNode(common()->Merge(count), count, &controls.front());
7675 effects.push_back(control);
7676 effect =
7677 graph()->NewNode(common()->EffectPhi(count), count + 1, &effects.front());
7678 values.push_back(control);
7679 Node* value =
7681 count + 1, &values.front());
7682 ReplaceWithValue(node, value, effect, control);
7683 return Replace(value);
7684}
7685
7687 Node* node, InstanceType instance_type, Builtin builtin) {
7688 // TODO(v8:11111): Optimize for JS_RAB_GSAB_DATA_VIEW_TYPE too.
7689 DCHECK(instance_type == JS_TYPED_ARRAY_TYPE ||
7690 instance_type == JS_DATA_VIEW_TYPE);
7694
7695 MapInference inference(broker(), receiver, effect);
7696 if (!inference.HaveMaps() ||
7697 !inference.AllOfInstanceTypesAre(instance_type)) {
7698 return inference.NoChange();
7699 }
7700
7701 std::set<ElementsKind> elements_kinds;
7702 bool maybe_rab_gsab = false;
7703 if (instance_type == JS_TYPED_ARRAY_TYPE) {
7704 for (MapRef map : inference.GetMaps()) {
7705 ElementsKind kind = map.elements_kind();
7706 elements_kinds.insert(kind);
7707 if (IsRabGsabTypedArrayElementsKind(kind)) maybe_rab_gsab = true;
7708 }
7709 }
7710
7711 if (!maybe_rab_gsab) {
7712 // We do not perform any change depending on this inference.
7713 Reduction unused_reduction = inference.NoChange();
7714 USE(unused_reduction);
7715 // Call default implementation for non-rab/gsab TAs.
7717 node, instance_type, AccessBuilder::ForJSArrayBufferViewByteLength(),
7718 builtin);
7719 }
7720
7721 const CallParameters& p = CallParametersOf(node->op());
7723 return inference.NoChange();
7724 }
7725 DCHECK(p.feedback().IsValid());
7726
7727 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
7728 control, p.feedback());
7729
7730 const bool depended_on_detaching_protector =
7732 if (!depended_on_detaching_protector && instance_type == JS_DATA_VIEW_TYPE) {
7733 // DataView prototype accessors throw on detached ArrayBuffers instead of
7734 // return 0, so skip the optimization.
7735 //
7736 // TODO(turbofan): Ideally we would bail out if the buffer is actually
7737 // detached.
7738 return inference.NoChange();
7739 }
7740
7741 JSCallReducerAssembler a(this, node);
7742 TNode<JSTypedArray> typed_array =
7744 TNode<Number> length = a.ArrayBufferViewByteLength(
7745 typed_array, instance_type, std::move(elements_kinds), a.ContextInput());
7746
7747 return ReplaceWithSubgraph(&a, length);
7748}
7749
7751 Node* node, InstanceType instance_type, Builtin builtin) {
7752 // TODO(v8:11111): Optimize for JS_RAB_GSAB_DATA_VIEW_TYPE too.
7753 DCHECK(instance_type == JS_TYPED_ARRAY_TYPE ||
7754 instance_type == JS_DATA_VIEW_TYPE);
7758
7759 MapInference inference(broker(), receiver, effect);
7760 if (!inference.HaveMaps() ||
7761 !inference.AllOfInstanceTypesAre(instance_type)) {
7762 return inference.NoChange();
7763 }
7764
7765 std::set<ElementsKind> elements_kinds;
7766 bool maybe_rab_gsab = false;
7767 if (instance_type == JS_TYPED_ARRAY_TYPE) {
7768 for (MapRef map : inference.GetMaps()) {
7769 ElementsKind kind = map.elements_kind();
7770 elements_kinds.insert(kind);
7771 if (IsRabGsabTypedArrayElementsKind(kind)) maybe_rab_gsab = true;
7772 }
7773 }
7774
7775 if (!maybe_rab_gsab) {
7776 // We do not perform any change depending on this inference.
7777 Reduction unused_reduction = inference.NoChange();
7778 USE(unused_reduction);
7779 // Call default implementation for non-rab/gsab TAs.
7781 node, instance_type, AccessBuilder::ForJSArrayBufferViewByteOffset(),
7782 builtin);
7783 }
7784
7785 // TODO(v8:11111): Optimize for RAG/GSAB TypedArray/DataView.
7786 return inference.NoChange();
7787}
7788
7793
7794 MapInference inference(broker(), receiver, effect);
7795 if (!inference.HaveMaps() ||
7796 !inference.AllOfInstanceTypesAre(JS_TYPED_ARRAY_TYPE)) {
7797 return inference.NoChange();
7798 }
7799
7800 std::set<ElementsKind> elements_kinds;
7801 bool maybe_rab_gsab = false;
7802 for (MapRef map : inference.GetMaps()) {
7803 ElementsKind kind = map.elements_kind();
7804 elements_kinds.insert(kind);
7805 if (IsRabGsabTypedArrayElementsKind(kind)) maybe_rab_gsab = true;
7806 }
7807
7808 if (!maybe_rab_gsab) {
7809 // We do not perform any change depending on this inference.
7810 Reduction unused_reduction = inference.NoChange();
7811 USE(unused_reduction);
7812 // Call default implementation for non-rab/gsab TAs.
7813 return ReduceArrayBufferViewAccessor(node, JS_TYPED_ARRAY_TYPE,
7815 Builtin::kTypedArrayPrototypeLength);
7816 }
7817
7818 if (!inference.RelyOnMapsViaStability(dependencies())) {
7819 return inference.NoChange();
7820 }
7821
7822 JSCallReducerAssembler a(this, node);
7823 TNode<JSTypedArray> typed_array =
7825 TNode<Number> length = a.TypedArrayLength(
7826 typed_array, std::move(elements_kinds), a.ContextInput());
7827
7828 if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
7829 length =
7830 a.MachineSelectIf<Number>(a.ArrayBufferViewDetachedBit(typed_array))
7831 .Then([&]() { return a.NumberConstant(0); })
7832 .Else([&]() { return length; })
7833 .ExpectFalse()
7834 .Value();
7835 }
7836
7837 return ReplaceWithSubgraph(&a, length);
7838}
7839
7840// ES #sec-number.isfinite
7842 JSCallNode n(node);
7843 if (n.ArgumentCount() < 1) {
7844 Node* value = jsgraph()->FalseConstant();
7845 ReplaceWithValue(node, value);
7846 return Replace(value);
7847 }
7848 Node* input = n.Argument(0);
7849 Node* value = graph()->NewNode(simplified()->ObjectIsFiniteNumber(), input);
7850 ReplaceWithValue(node, value);
7851 return Replace(value);
7852}
7853
7854// ES #sec-number.isfinite
7856 JSCallNode n(node);
7857 if (n.ArgumentCount() < 1) {
7858 Node* value = jsgraph()->FalseConstant();
7859 ReplaceWithValue(node, value);
7860 return Replace(value);
7861 }
7862 Node* input = n.Argument(0);
7863 Node* value = graph()->NewNode(simplified()->ObjectIsInteger(), input);
7864 ReplaceWithValue(node, value);
7865 return Replace(value);
7866}
7867
7868// ES #sec-number.issafeinteger
7870 JSCallNode n(node);
7871 if (n.ArgumentCount() < 1) {
7872 Node* value = jsgraph()->FalseConstant();
7873 ReplaceWithValue(node, value);
7874 return Replace(value);
7875 }
7876 Node* input = n.Argument(0);
7877 Node* value = graph()->NewNode(simplified()->ObjectIsSafeInteger(), input);
7878 ReplaceWithValue(node, value);
7879 return Replace(value);
7880}
7881
7882// ES #sec-number.isnan
7884 JSCallNode n(node);
7885 if (n.ArgumentCount() < 1) {
7886 Node* value = jsgraph()->FalseConstant();
7887 ReplaceWithValue(node, value);
7888 return Replace(value);
7889 }
7890 Node* input = n.Argument(0);
7891 Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input);
7892 ReplaceWithValue(node, value);
7893 return Replace(value);
7894}
7895
7897 // We only optimize if we have target, receiver and key parameters.
7898 JSCallNode n(node);
7899 if (n.ArgumentCount() != 1) return NoChange();
7904
7905 MapInference inference(broker(), receiver, effect);
7906 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAre(JS_MAP_TYPE)) {
7907 return NoChange();
7908 }
7909
7910 Node* table = effect = graph()->NewNode(
7912 effect, control);
7913
7914 Node* entry = effect = graph()->NewNode(
7915 simplified()->FindOrderedCollectionEntry(CollectionKind::kMap), table,
7916 key, effect, control);
7917
7918 Node* check = graph()->NewNode(simplified()->NumberEqual(), entry,
7919 jsgraph()->MinusOneConstant());
7920
7921 Node* branch = graph()->NewNode(common()->Branch(), check, control);
7922
7923 // Key not found.
7924 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
7925 Node* etrue = effect;
7926 Node* vtrue = jsgraph()->UndefinedConstant();
7927
7928 // Key found.
7929 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
7930 Node* efalse = effect;
7931 Node* vfalse = efalse = graph()->NewNode(
7933 table, entry, efalse, if_false);
7934
7935 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
7936 Node* value = graph()->NewNode(
7937 common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);
7938 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
7939
7940 ReplaceWithValue(node, value, effect, control);
7941 return Replace(value);
7942}
7943
7944namespace {
7945
7946InstanceType InstanceTypeForCollectionKind(CollectionKind kind) {
7947 switch (kind) {
7949 return JS_MAP_TYPE;
7951 return JS_SET_TYPE;
7952 }
7953 UNREACHABLE();
7954}
7955
7956} // namespace
7957
7959 Node* node, CollectionKind collection_kind) {
7960 // We only optimize if we have target, receiver and key parameters.
7961 JSCallNode n(node);
7962 if (n.ArgumentCount() != 1) return NoChange();
7967 InstanceType instance_type = InstanceTypeForCollectionKind(collection_kind);
7968
7969 MapInference inference(broker(), receiver, effect);
7970 if (!inference.HaveMaps() ||
7971 !inference.AllOfInstanceTypesAre(instance_type)) {
7972 return NoChange();
7973 }
7974
7975 Node* table = effect = graph()->NewNode(
7977 effect, control);
7978
7979 Node* index = effect = graph()->NewNode(
7980 simplified()->FindOrderedCollectionEntry(collection_kind), table, key,
7981 effect, control);
7982
7983 Node* value = graph()->NewNode(simplified()->NumberEqual(), index,
7984 jsgraph()->MinusOneConstant());
7985 value = graph()->NewNode(simplified()->BooleanNot(), value);
7986
7987 ReplaceWithValue(node, value, effect, control);
7988 return Replace(value);
7989}
7990
7994
7998
8000 Node* node, CollectionKind collection_kind, IterationKind iteration_kind) {
8001 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
8003 Node* context = NodeProperties::GetContextInput(node);
8006
8007 InstanceType type = InstanceTypeForCollectionKind(collection_kind);
8008 MapInference inference(broker(), receiver, effect);
8009 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAre(type)) {
8010 return NoChange();
8011 }
8012
8013 Node* js_create_iterator = effect = graph()->NewNode(
8014 javascript()->CreateCollectionIterator(collection_kind, iteration_kind),
8015 receiver, context, effect, control);
8016 ReplaceWithValue(node, js_create_iterator, effect);
8017 return Replace(js_create_iterator);
8018}
8019
8021 Node* node, CollectionKind collection_kind) {
8022 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
8026
8027 InstanceType type = InstanceTypeForCollectionKind(collection_kind);
8028 MapInference inference(broker(), receiver, effect);
8029 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAre(type)) {
8030 return NoChange();
8031 }
8032
8033 Node* table = effect = graph()->NewNode(
8035 effect, control);
8036 Node* value = effect = graph()->NewNode(
8037 simplified()->LoadField(
8039 table, effect, control);
8040 ReplaceWithValue(node, value, effect, control);
8041 return Replace(value);
8042}
8043
8045 Node* node, int entry_size, Handle<HeapObject> empty_collection,
8046 InstanceType collection_iterator_instance_type_first,
8047 InstanceType collection_iterator_instance_type_last) {
8048 JSCallNode n(node);
8049 CallParameters const& p = n.Parameters();
8051 return NoChange();
8052 }
8053
8054 Node* receiver = n.receiver();
8055 Node* context = n.context();
8056 Effect effect = n.effect();
8057 Control control = n.control();
8058
8059 // A word of warning to begin with: This whole method might look a bit
8060 // strange at times, but that's mostly because it was carefully handcrafted
8061 // to allow for full escape analysis and scalar replacement of both the
8062 // collection iterator object and the iterator results, including the
8063 // key-value arrays in case of Set/Map entry iteration.
8064 //
8065 // TODO(turbofan): Currently the escape analysis (and the store-load
8066 // forwarding) is unable to eliminate the allocations for the key-value
8067 // arrays in case of Set/Map entry iteration, and we should investigate
8068 // how to update the escape analysis / arrange the graph in a way that
8069 // this becomes possible.
8070
8071 InstanceType receiver_instance_type;
8072 {
8073 MapInference inference(broker(), receiver, effect);
8074 if (!inference.HaveMaps()) return NoChange();
8075 ZoneRefSet<Map> const& receiver_maps = inference.GetMaps();
8076 receiver_instance_type = receiver_maps[0].instance_type();
8077 for (size_t i = 1; i < receiver_maps.size(); ++i) {
8078 if (receiver_maps[i].instance_type() != receiver_instance_type) {
8079 return inference.NoChange();
8080 }
8081 }
8082 if (receiver_instance_type < collection_iterator_instance_type_first ||
8083 receiver_instance_type > collection_iterator_instance_type_last) {
8084 return inference.NoChange();
8085 }
8086 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
8087 control, p.feedback());
8088 }
8089
8090 // Transition the JSCollectionIterator {receiver} if necessary
8091 // (i.e. there were certain mutations while we're iterating).
8092 {
8093 Node* done_loop;
8094 Node* done_eloop;
8095 Node* loop = control =
8096 graph()->NewNode(common()->Loop(2), control, control);
8097 Node* eloop = effect =
8098 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
8099 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
8100 MergeControlToEnd(graph(), common(), terminate);
8101
8102 // Check if reached the final table of the {receiver}.
8103 Node* table = effect = graph()->NewNode(
8105 receiver, effect, control);
8106 Node* next_table = effect =
8107 graph()->NewNode(simplified()->LoadField(
8109 table, effect, control);
8110 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table);
8111 control =
8112 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
8113
8114 // Abort the {loop} when we reach the final table.
8115 done_loop = graph()->NewNode(common()->IfTrue(), control);
8116 done_eloop = effect;
8117
8118 // Migrate to the {next_table} otherwise.
8119 control = graph()->NewNode(common()->IfFalse(), control);
8120
8121 // Self-heal the {receiver}s index.
8122 Node* index = effect = graph()->NewNode(
8124 receiver, effect, control);
8125 Callable const callable =
8126 Builtins::CallableFor(isolate(), Builtin::kOrderedHashTableHealIndex);
8127 auto call_descriptor = Linkage::GetStubCallDescriptor(
8128 graph()->zone(), callable.descriptor(),
8131 index = effect =
8132 graph()->NewNode(common()->Call(call_descriptor),
8133 jsgraph()->HeapConstantNoHole(callable.code()), table,
8134 index, jsgraph()->NoContextConstant(), effect);
8135
8136 index = effect = graph()->NewNode(
8137 common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), index,
8138 effect, control);
8139
8140 // Update the {index} and {table} on the {receiver}.
8141 effect = graph()->NewNode(
8143 receiver, index, effect, control);
8144 effect = graph()->NewNode(
8146 receiver, next_table, effect, control);
8147
8148 // Tie the knot.
8149 loop->ReplaceInput(1, control);
8150 eloop->ReplaceInput(1, effect);
8151
8152 control = done_loop;
8153 effect = done_eloop;
8154 }
8155
8156 // Get current index and table from the JSCollectionIterator {receiver}.
8157 Node* index = effect = graph()->NewNode(
8159 receiver, effect, control);
8160 Node* table = effect = graph()->NewNode(
8162 receiver, effect, control);
8163
8164 // Create the {JSIteratorResult} first to ensure that we always have
8165 // a dominating Allocate node for the allocation folding phase.
8166 Node* iterator_result = effect = graph()->NewNode(
8167 javascript()->CreateIterResultObject(), jsgraph()->UndefinedConstant(),
8168 jsgraph()->TrueConstant(), context, effect);
8169
8170 // Look for the next non-holey key, starting from {index} in the {table}.
8171 Node* controls[2];
8172 Node* effects[3];
8173 {
8174 // Compute the currently used capacity.
8175 Node* number_of_buckets = effect = graph()->NewNode(
8176 simplified()->LoadField(
8178 table, effect, control);
8179 Node* number_of_elements = effect = graph()->NewNode(
8180 simplified()->LoadField(
8182 table, effect, control);
8183 Node* number_of_deleted_elements = effect = graph()->NewNode(
8184 simplified()->LoadField(
8186 table, effect, control);
8187 Node* used_capacity =
8188 graph()->NewNode(simplified()->NumberAdd(), number_of_elements,
8189 number_of_deleted_elements);
8190
8191 // Skip holes and update the {index}.
8192 Node* loop = graph()->NewNode(common()->Loop(2), control, control);
8193 Node* eloop =
8194 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
8195 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
8196 MergeControlToEnd(graph(), common(), terminate);
8197 Node* iloop = graph()->NewNode(
8198 common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop);
8199
8200 index = effect = graph()->NewNode(
8201 common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), iloop,
8202 eloop, control);
8203 {
8204 Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), index,
8205 used_capacity);
8206 Node* branch0 =
8207 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, loop);
8208
8209 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
8210 Node* efalse0 = effect;
8211 {
8212 // Mark the {receiver} as exhausted.
8213 efalse0 = graph()->NewNode(
8214 simplified()->StoreField(
8216 receiver, jsgraph()->HeapConstantNoHole(empty_collection), efalse0,
8217 if_false0);
8218
8219 controls[0] = if_false0;
8220 effects[0] = efalse0;
8221 }
8222
8223 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
8224 Node* etrue0 = effect;
8225 {
8226 // Load the key of the entry.
8227 static_assert(OrderedHashMap::HashTableStartIndex() ==
8229 Node* entry_start_position = graph()->NewNode(
8230 simplified()->NumberAdd(),
8231 graph()->NewNode(
8232 simplified()->NumberAdd(),
8233 graph()->NewNode(simplified()->NumberMultiply(), index,
8234 jsgraph()->ConstantNoHole(entry_size)),
8235 number_of_buckets),
8236 jsgraph()->ConstantNoHole(OrderedHashMap::HashTableStartIndex()));
8237 Node* entry_key = etrue0 = graph()->NewNode(
8239 table, entry_start_position, etrue0, if_true0);
8240
8241 // Advance the index.
8242 index = graph()->NewNode(simplified()->NumberAdd(), index,
8243 jsgraph()->OneConstant());
8244
8245 Node* check1 =
8246 graph()->NewNode(simplified()->ReferenceEqual(), entry_key,
8247 jsgraph()->HashTableHoleConstant());
8248 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
8249 check1, if_true0);
8250
8251 {
8252 // Abort loop with resulting value.
8253 control = graph()->NewNode(common()->IfFalse(), branch1);
8254 effect = etrue0;
8255 Node* value = effect =
8256 graph()->NewNode(common()->TypeGuard(Type::NonInternal()),
8257 entry_key, effect, control);
8258 Node* done = jsgraph()->FalseConstant();
8259
8260 // Advance the index on the {receiver}.
8261 effect = graph()->NewNode(
8262 simplified()->StoreField(
8264 receiver, index, effect, control);
8265
8266 // The actual {value} depends on the {receiver} iteration type.
8267 switch (receiver_instance_type) {
8268 case JS_MAP_KEY_ITERATOR_TYPE:
8269 case JS_SET_VALUE_ITERATOR_TYPE:
8270 break;
8271
8272 case JS_SET_KEY_VALUE_ITERATOR_TYPE:
8273 value = effect =
8274 graph()->NewNode(javascript()->CreateKeyValueArray(), value,
8275 value, context, effect);
8276 break;
8277
8278 case JS_MAP_VALUE_ITERATOR_TYPE:
8279 value = effect = graph()->NewNode(
8280 simplified()->LoadElement(
8282 table,
8283 graph()->NewNode(
8284 simplified()->NumberAdd(), entry_start_position,
8285 jsgraph()->ConstantNoHole(OrderedHashMap::kValueOffset)),
8286 effect, control);
8287 break;
8288
8289 case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
8290 value = effect = graph()->NewNode(
8291 simplified()->LoadElement(
8293 table,
8294 graph()->NewNode(
8295 simplified()->NumberAdd(), entry_start_position,
8296 jsgraph()->ConstantNoHole(OrderedHashMap::kValueOffset)),
8297 effect, control);
8298 value = effect =
8299 graph()->NewNode(javascript()->CreateKeyValueArray(),
8300 entry_key, value, context, effect);
8301 break;
8302
8303 default:
8304 UNREACHABLE();
8305 }
8306
8307 // Store final {value} and {done} into the {iterator_result}.
8308 effect =
8309 graph()->NewNode(simplified()->StoreField(
8311 iterator_result, value, effect, control);
8312 effect =
8313 graph()->NewNode(simplified()->StoreField(
8315 iterator_result, done, effect, control);
8316
8317 controls[1] = control;
8318 effects[1] = effect;
8319 }
8320
8321 // Continue with next loop index.
8322 loop->ReplaceInput(1, graph()->NewNode(common()->IfTrue(), branch1));
8323 eloop->ReplaceInput(1, etrue0);
8324 iloop->ReplaceInput(1, index);
8325 }
8326 }
8327
8328 control = effects[2] = graph()->NewNode(common()->Merge(2), 2, controls);
8329 effect = graph()->NewNode(common()->EffectPhi(2), 3, effects);
8330 }
8331
8332 // Yield the final {iterator_result}.
8333 ReplaceWithValue(node, iterator_result, effect, control);
8334 return Replace(iterator_result);
8335}
8336
8338 JSCallNode n(node);
8339 Node* value = n.ArgumentOrUndefined(0, jsgraph());
8341 node->ReplaceInput(0, value);
8342 node->TrimInputCount(1);
8343 NodeProperties::ChangeOp(node, simplified()->ObjectIsArrayBufferView());
8344 return Changed(node);
8345}
8346
8348 Node* node, InstanceType instance_type, FieldAccess const& access,
8349 Builtin builtin) {
8350 // TODO(v8:11111): Optimize for JS_RAB_GSAB_DATA_VIEW_TYPE too.
8354
8355 MapInference inference(broker(), receiver, effect);
8356 if (!inference.HaveMaps() ||
8357 !inference.AllOfInstanceTypesAre(instance_type)) {
8358 return inference.NoChange();
8359 }
8360
8361 DCHECK_IMPLIES((builtin == Builtin::kTypedArrayPrototypeLength ||
8362 builtin == Builtin::kTypedArrayPrototypeByteLength),
8363 base::none_of(inference.GetMaps(), [](MapRef map) {
8364 return IsRabGsabTypedArrayElementsKind(map.elements_kind());
8365 }));
8366
8367 if (!inference.RelyOnMapsViaStability(dependencies())) {
8368 return inference.NoChange();
8369 }
8370
8371 const bool depended_on_detaching_protector =
8373 if (!depended_on_detaching_protector && instance_type == JS_DATA_VIEW_TYPE) {
8374 // DataView prototype accessors throw on detached ArrayBuffers instead of
8375 // return 0, so skip the optimization.
8376 //
8377 // TODO(turbofan): Ideally we would bail out if the buffer is actually
8378 // detached.
8379 return inference.NoChange();
8380 }
8381
8382 JSCallReducerAssembler a(this, node, effect);
8383
8384 // Load the {receiver}s field.
8385 DCHECK_EQ(access.machine_type.representation(),
8387 TNode<UintPtrT> value = a.EnterMachineGraph<UintPtrT>(
8388 a.LoadField<UintPtrT>(access, receiver), UseInfo::Word());
8389
8390 // See if we can skip the detaching check.
8391 if (!depended_on_detaching_protector) {
8392 // Check whether {receiver}s JSArrayBuffer was detached.
8393 //
8394 // TODO(turbofan): Ideally we would bail out here if the {receiver}s
8395 // JSArrayBuffer was detached, but there's no way to guard against
8396 // deoptimization loops right now, since the JSCall {node} is usually
8397 // created from a LOAD_IC inlining, and so there's no CALL_IC slot
8398 // from which we could use the speculation bit.
8399 value = a.MachineSelect<UintPtrT>(a.ArrayBufferViewDetachedBit(receiver),
8400 a.UintPtrConstant(0), value,
8402 }
8403
8405 a.ExitMachineGraph<Number>(value, MachineType::PointerRepresentation(),
8407 return ReplaceWithSubgraph(&a, result);
8408}
8409
8411 ExternalArrayType element_type) {
8412 // TODO(v8:11111): Optimize for JS_RAB_GSAB_DATA_VIEW_TYPE too.
8413 JSCallNode n(node);
8414 CallParameters const& p = n.Parameters();
8415 size_t const element_size = ExternalArrayElementSize(element_type);
8416 Effect effect = n.effect();
8417 Control control = n.control();
8418 Node* receiver = n.receiver();
8419 Node* offset = n.ArgumentOr(0, jsgraph()->ZeroConstant());
8420 Node* value = nullptr;
8421
8422 if (!Is64() && (element_type == kExternalBigInt64Array ||
8423 element_type == kExternalBigUint64Array)) {
8424 return NoChange();
8425 }
8426
8427 if (access == DataViewAccess::kSet) {
8428 value = n.ArgumentOrUndefined(1, jsgraph());
8429 }
8430 const int endian_index = (access == DataViewAccess::kGet ? 1 : 2);
8431 Node* is_little_endian =
8432 n.ArgumentOr(endian_index, jsgraph()->FalseConstant());
8433
8435 return NoChange();
8436 }
8437
8438 // Only do stuff if the {receiver} is really a DataView.
8439 MapInference inference(broker(), receiver, effect);
8440 if (!inference.HaveMaps() ||
8441 !inference.AllOfInstanceTypesAre(JS_DATA_VIEW_TYPE)) {
8442 return NoChange();
8443 }
8444
8445 // Check that the {offset} is within range for the {receiver}.
8447 if (m.HasResolvedValue() && m.Ref(broker()).IsJSDataView()) {
8448 // We only deal with DataViews here whose [[ByteLength]] is at least
8449 // {element_size}, as for all other DataViews it'll be out-of-bounds.
8450 JSDataViewRef dataview = m.Ref(broker()).AsJSDataView();
8451
8452 size_t length = dataview.byte_length();
8453 if (length < element_size) return NoChange();
8454
8455 // Check that the {offset} is within range of the {length}.
8456 Node* byte_length = jsgraph()->ConstantNoHole(length - (element_size - 1));
8457 offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
8458 offset, byte_length, effect, control);
8459 } else {
8460 // We only deal with DataViews here that have Smi [[ByteLength]]s.
8461 Node* byte_length = effect =
8462 graph()->NewNode(simplified()->LoadField(
8464 receiver, effect, control);
8465 if (element_size > 1) {
8466 // For non-byte accesses we also need to check that the {offset}
8467 // plus the {element_size}-1 fits within the given {byte_length}.
8468 // So to keep this as a single check on the {offset}, we subtract
8469 // the {element_size}-1 from the {byte_length} here (clamped to
8470 // positive safe integer range), and perform a check against that
8471 // with the {offset} below.
8472 byte_length = graph()->NewNode(
8473 simplified()->NumberMax(), jsgraph()->ZeroConstant(),
8474 graph()->NewNode(simplified()->NumberSubtract(), byte_length,
8475 jsgraph()->ConstantNoHole(element_size - 1)));
8476 }
8477
8478 // Check that the {offset} is within range of the {byte_length}.
8479 offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
8480 offset, byte_length, effect, control);
8481 }
8482
8483 // Coerce {is_little_endian} to boolean.
8484 is_little_endian =
8485 graph()->NewNode(simplified()->ToBoolean(), is_little_endian);
8486
8487 // Coerce {value} to Number.
8488 if (access == DataViewAccess::kSet) {
8489 if (element_type == kExternalBigInt64Array ||
8490 element_type == kExternalBigUint64Array) {
8491 value = effect =
8492 graph()->NewNode(simplified()->SpeculativeToBigInt(
8494 value, effect, control);
8495 } else {
8496 value = effect = graph()->NewNode(
8497 simplified()->SpeculativeToNumber(
8499 value, effect, control);
8500 }
8501 }
8502
8503 // We need to retain either the {receiver} itself or its backing
8504 // JSArrayBuffer to make sure that the GC doesn't collect the raw
8505 // memory. We default to {receiver} here, and only use the buffer
8506 // if we anyways have to load it (to reduce register pressure).
8507 Node* buffer_or_receiver = receiver;
8508
8509 if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
8510 // Get the underlying buffer and check that it has not been detached.
8511 Node* buffer = effect = graph()->NewNode(
8513 receiver, effect, control);
8514
8515 // Bail out if the {buffer} was detached.
8516 Node* buffer_bit_field = effect = graph()->NewNode(
8518 buffer, effect, control);
8519 Node* check = graph()->NewNode(
8520 simplified()->NumberEqual(),
8521 graph()->NewNode(
8522 simplified()->NumberBitwiseAnd(), buffer_bit_field,
8523 jsgraph()->ConstantNoHole(JSArrayBuffer::WasDetachedBit::kMask)),
8524 jsgraph()->ZeroConstant());
8525 effect = graph()->NewNode(
8526 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached,
8527 p.feedback()),
8528 check, effect, control);
8529
8530 // We can reduce register pressure by holding on to the {buffer}
8531 // now to retain the backing store memory.
8532 buffer_or_receiver = buffer;
8533 }
8534
8535 // Load the {receiver}s data pointer.
8536 Node* data_pointer = effect = graph()->NewNode(
8538 receiver, effect, control);
8539
8540 switch (access) {
8542 // Perform the load.
8543 value = effect = graph()->NewNode(
8544 simplified()->LoadDataViewElement(element_type), buffer_or_receiver,
8545 data_pointer, offset, is_little_endian, effect, control);
8546 break;
8548 // Perform the store.
8549 effect = graph()->NewNode(
8550 simplified()->StoreDataViewElement(element_type), buffer_or_receiver,
8551 data_pointer, offset, value, is_little_endian, effect, control);
8552 value = jsgraph()->UndefinedConstant();
8553 break;
8554 }
8555
8556 ReplaceWithValue(node, value, effect, control);
8557 return Changed(value);
8558}
8559
8560// ES6 section 18.2.2 isFinite ( number )
8562 JSCallNode n(node);
8563 CallParameters const& p = n.Parameters();
8565 return NoChange();
8566 }
8567 if (n.ArgumentCount() < 1) {
8568 Node* value = jsgraph()->FalseConstant();
8569 ReplaceWithValue(node, value);
8570 return Replace(value);
8571 }
8572
8573 Effect effect = n.effect();
8574 Control control = n.control();
8575 Node* input = n.Argument(0);
8576
8577 input = effect =
8578 graph()->NewNode(simplified()->SpeculativeToNumber(
8580 input, effect, control);
8581 Node* value = graph()->NewNode(simplified()->NumberIsFinite(), input);
8582 ReplaceWithValue(node, value, effect);
8583 return Replace(value);
8584}
8585
8586// ES6 section 18.2.3 isNaN ( number )
8588 JSCallNode n(node);
8589 CallParameters const& p = n.Parameters();
8591 return NoChange();
8592 }
8593 if (n.ArgumentCount() < 1) {
8594 Node* value = jsgraph()->TrueConstant();
8595 ReplaceWithValue(node, value);
8596 return Replace(value);
8597 }
8598
8599 Effect effect = n.effect();
8600 Control control = n.control();
8601 Node* input = n.Argument(0);
8602
8603 input = effect =
8604 graph()->NewNode(simplified()->SpeculativeToNumber(
8606 input, effect, control);
8607 Node* value = graph()->NewNode(simplified()->NumberIsNaN(), input);
8608 ReplaceWithValue(node, value, effect);
8609 return Replace(value);
8610}
8611
8612// ES6 section 20.3.4.10 Date.prototype.getTime ( )
8617
8618 MapInference inference(broker(), receiver, effect);
8619 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAre(JS_DATE_TYPE)) {
8620 return NoChange();
8621 }
8622
8623 Node* value = effect =
8625 receiver, effect, control);
8626 ReplaceWithValue(node, value, effect, control);
8627 return Replace(value);
8628}
8629
8630// ES6 section 20.3.3.1 Date.now ( )
8632 Node* effect = NodeProperties::GetEffectInput(node);
8633 Node* control = NodeProperties::GetControlInput(node);
8634 Node* value = effect =
8635 graph()->NewNode(simplified()->DateNow(), effect, control);
8636 ReplaceWithValue(node, value, effect, control);
8637 return Replace(value);
8638}
8639
8640// ES6 section 20.1.2.13 Number.parseInt ( string, radix )
8642 JSCallNode n(node);
8643 if (n.ArgumentCount() < 1) {
8644 Node* value = jsgraph()->NaNConstant();
8645 ReplaceWithValue(node, value);
8646 return Replace(value);
8647 }
8648
8649 Effect effect = n.effect();
8650 Control control = n.control();
8651 Node* context = n.context();
8652 FrameState frame_state = n.frame_state();
8653 Node* object = n.Argument(0);
8654 Node* radix = n.ArgumentOrUndefined(1, jsgraph());
8655
8656 // Try constant-folding when input is a string constant.
8657 HeapObjectMatcher object_matcher(object);
8658 HeapObjectMatcher radix_object_matcher(radix);
8659 NumberMatcher radix_number_matcher(radix);
8660 if (object_matcher.HasResolvedValue() &&
8661 object_matcher.Ref(broker()).IsString() &&
8662 (radix_object_matcher.Is(factory()->undefined_value()) ||
8663 radix_number_matcher.HasResolvedValue())) {
8664 StringRef input_value = object_matcher.Ref(broker()).AsString();
8665 // {undefined} is treated same as 0.
8666 int radix_value = radix_object_matcher.Is(factory()->undefined_value())
8667 ? 0
8668 : DoubleToInt32(radix_number_matcher.ResolvedValue());
8669 if (radix_value != 0 && (radix_value < 2 || radix_value > 36)) {
8670 Node* value = jsgraph()->NaNConstant();
8671 ReplaceWithValue(node, value);
8672 return Replace(value);
8673 }
8674
8675 std::optional<double> number = input_value.ToInt(broker(), radix_value);
8676 if (number.has_value()) {
8677 Node* result = graph()->NewNode(common()->NumberConstant(number.value()));
8678 ReplaceWithValue(node, result);
8679 return Replace(result);
8680 }
8681 }
8682
8683 node->ReplaceInput(0, object);
8684 node->ReplaceInput(1, radix);
8685 node->ReplaceInput(2, context);
8686 node->ReplaceInput(3, frame_state);
8687 node->ReplaceInput(4, effect);
8688 node->ReplaceInput(5, control);
8689 node->TrimInputCount(6);
8690 NodeProperties::ChangeOp(node, javascript()->ParseInt());
8691 return Changed(node);
8692}
8693
8695 JSCallNode n(node);
8696 CallParameters const& p = n.Parameters();
8697 if (v8_flags.force_slow_path) return NoChange();
8698 if (n.ArgumentCount() < 1) return NoChange();
8699
8701 return NoChange();
8702 }
8703
8704 Effect effect = n.effect();
8705 Control control = n.control();
8706 Node* regexp = n.receiver();
8707
8708 // Only the initial JSRegExp map is valid here, since the following lastIndex
8709 // check as well as the lowered builtin call rely on a known location of the
8710 // lastIndex field.
8711 MapRef regexp_initial_map =
8712 native_context().regexp_function(broker()).initial_map(broker());
8713
8714 MapInference inference(broker(), regexp, effect);
8715 if (!inference.Is(regexp_initial_map)) return inference.NoChange();
8716 ZoneRefSet<Map> const& regexp_maps = inference.GetMaps();
8717
8718 ZoneVector<PropertyAccessInfo> access_infos(graph()->zone());
8719 AccessInfoFactory access_info_factory(broker(), graph()->zone());
8720
8721 for (MapRef map : regexp_maps) {
8722 access_infos.push_back(broker()->GetPropertyAccessInfo(
8723 map, broker()->exec_string(), AccessMode::kLoad));
8724 }
8725
8726 PropertyAccessInfo ai_exec =
8727 access_info_factory.FinalizePropertyAccessInfosAsOne(access_infos,
8729 if (ai_exec.IsInvalid()) return inference.NoChange();
8730 if (!ai_exec.IsFastDataConstant()) return inference.NoChange();
8731
8732 // Do not reduce if the exec method is not on the prototype chain.
8733 OptionalJSObjectRef holder = ai_exec.holder();
8734 if (!holder.has_value()) return inference.NoChange();
8735
8736 // Bail out if the exec method is not the original one.
8737 if (ai_exec.field_representation().IsDouble()) return inference.NoChange();
8738 OptionalObjectRef constant = holder->GetOwnFastConstantDataProperty(
8739 broker(), ai_exec.field_representation(), ai_exec.field_index(),
8740 dependencies());
8741 if (!constant.has_value() ||
8742 !constant->equals(native_context().regexp_exec_function(broker()))) {
8743 return inference.NoChange();
8744 }
8745
8746 // Add proper dependencies on the {regexp}s [[Prototype]]s.
8748 ai_exec.lookup_start_object_maps(), kStartAtPrototype, holder.value());
8749
8750 inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
8751 control, p.feedback());
8752
8753 Node* context = n.context();
8754 FrameState frame_state = n.frame_state();
8755 Node* search = n.Argument(0);
8756 Node* search_string = effect = graph()->NewNode(
8757 simplified()->CheckString(p.feedback()), search, effect, control);
8758
8759 Node* lastIndex = effect = graph()->NewNode(
8760 simplified()->LoadField(AccessBuilder::ForJSRegExpLastIndex()), regexp,
8761 effect, control);
8762
8763 Node* lastIndexSmi = effect = graph()->NewNode(
8764 simplified()->CheckSmi(p.feedback()), lastIndex, effect, control);
8765
8766 Node* is_positive = graph()->NewNode(simplified()->NumberLessThanOrEqual(),
8767 jsgraph()->ZeroConstant(), lastIndexSmi);
8768
8769 effect = graph()->NewNode(
8770 simplified()->CheckIf(DeoptimizeReason::kNotASmi, p.feedback()),
8771 is_positive, effect, control);
8772
8773 node->ReplaceInput(0, regexp);
8774 node->ReplaceInput(1, search_string);
8775 node->ReplaceInput(2, context);
8776 node->ReplaceInput(3, frame_state);
8777 node->ReplaceInput(4, effect);
8778 node->ReplaceInput(5, control);
8779 node->TrimInputCount(6);
8780 NodeProperties::ChangeOp(node, javascript()->RegExpTest());
8781 return Changed(node);
8782}
8783
8784// ES section #sec-number-constructor
8786 JSCallNode n(node);
8787 Node* target = n.target();
8788 Node* receiver = n.receiver();
8789 Node* value = n.ArgumentOr(0, jsgraph()->ZeroConstant());
8790 Node* context = n.context();
8791 FrameState frame_state = n.frame_state();
8792
8793 // Create the artificial frame state in the middle of the Number constructor.
8794 SharedFunctionInfoRef shared_info =
8795 native_context().number_function(broker()).shared(broker());
8796 Node* continuation_frame_state = CreateGenericLazyDeoptContinuationFrameState(
8797 jsgraph(), shared_info, target, context, receiver, frame_state);
8798
8799 // Convert the {value} to a Number.
8801 NodeProperties::ChangeOp(node, javascript()->ToNumberConvertBigInt());
8802 NodeProperties::ReplaceFrameStateInput(node, continuation_frame_state);
8803 return Changed(node);
8804}
8805
8806// ES section #sec-bigint-constructor
8808 if (!jsgraph()->machine()->Is64()) return NoChange();
8809
8810 JSCallNode n(node);
8811 if (n.ArgumentCount() < 1) {
8812 return NoChange();
8813 }
8814
8815 Node* target = n.target();
8816 Node* receiver = n.receiver();
8817 Node* value = n.Argument(0);
8818 Node* context = n.context();
8819 FrameState frame_state = n.frame_state();
8820
8821 // Create the artificial frame state in the middle of the BigInt constructor.
8822 SharedFunctionInfoRef shared_info =
8823 native_context().bigint_function(broker()).shared(broker());
8824 Node* continuation_frame_state = CreateGenericLazyDeoptContinuationFrameState(
8825 jsgraph(), shared_info, target, context, receiver, frame_state);
8826
8827 // Convert the {value} to a BigInt.
8829 NodeProperties::ChangeOp(node, javascript()->ToBigIntConvertNumber());
8830 NodeProperties::ReplaceFrameStateInput(node, continuation_frame_state);
8831 return Changed(node);
8832}
8833
8835 DCHECK(builtin == Builtin::kBigIntAsIntN ||
8836 builtin == Builtin::kBigIntAsUintN);
8837
8838 if (!jsgraph()->machine()->Is64()) return NoChange();
8839
8840 JSCallNode n(node);
8841 CallParameters const& p = n.Parameters();
8843 return NoChange();
8844 }
8845 if (n.ArgumentCount() < 2) {
8846 return NoChange();
8847 }
8848
8849 Effect effect = n.effect();
8850 Control control = n.control();
8851 Node* bits = n.Argument(0);
8852 Node* value = n.Argument(1);
8853
8854 NumberMatcher matcher(bits);
8855 if (matcher.IsInteger() && matcher.IsInRange(0, 64)) {
8856 const int bits_value = static_cast<int>(matcher.ResolvedValue());
8857 value = effect = graph()->NewNode(
8858 (builtin == Builtin::kBigIntAsIntN
8859 ? simplified()->SpeculativeBigIntAsIntN(bits_value, p.feedback())
8860 : simplified()->SpeculativeBigIntAsUintN(bits_value,
8861 p.feedback())),
8862 value, effect, control);
8863 ReplaceWithValue(node, value, effect);
8864 return Replace(value);
8865 }
8866
8867 return NoChange();
8868}
8869
8871 Node* node) {
8872 if (!v8_flags.turbo_optimize_math_minmax) return std::nullopt;
8873
8875 CallParameters const& p = n.Parameters();
8876 Node* target = n.target();
8877 Effect effect = n.effect();
8878 Control control = n.control();
8879
8881 return std::nullopt;
8882 }
8883
8884 if (n.ArgumentCount() != 1) {
8885 return std::nullopt;
8886 }
8887
8888 if (!dependencies()->DependOnNoElementsProtector()) {
8889 return std::nullopt;
8890 }
8891
8892 // These ops are handled by ReduceCallOrConstructWithArrayLikeOrSpread.
8893 // IrOpcode::kJSCreateEmptyLiteralArray is not included, since arguments_list
8894 // for Math.min/min is not likely to keep empty.
8895 Node* arguments_list = n.Argument(0);
8896 if (arguments_list->opcode() == IrOpcode::kJSCreateLiteralArray ||
8897 arguments_list->opcode() == IrOpcode::kJSCreateArguments) {
8898 return std::nullopt;
8899 }
8900
8901 HeapObjectMatcher m(target);
8902 if (m.HasResolvedValue()) {
8903 ObjectRef target_ref = m.Ref(broker());
8904 if (target_ref.IsJSFunction()) {
8905 JSFunctionRef function = target_ref.AsJSFunction();
8906
8907 // Don't inline cross native context.
8908 if (!function.native_context(broker()).equals(native_context())) {
8909 return std::nullopt;
8910 }
8911
8912 SharedFunctionInfoRef shared = function.shared(broker());
8913 Builtin builtin =
8914 shared.HasBuiltinId() ? shared.builtin_id() : Builtin::kNoBuiltinId;
8915 if (builtin == Builtin::kMathMax || builtin == Builtin::kMathMin) {
8916 return ReduceJSCallMathMinMaxWithArrayLike(node, builtin);
8917 } else {
8918 return std::nullopt;
8919 }
8920 }
8921 }
8922
8923 // Try specialize the JSCallWithArrayLike node with feedback target.
8924 if (ShouldUseCallICFeedback(target) &&
8926 p.feedback().IsValid()) {
8927 ProcessedFeedback const& feedback =
8929 if (feedback.IsInsufficient()) {
8930 return std::nullopt;
8931 }
8932 OptionalHeapObjectRef feedback_target = feedback.AsCall().target();
8933 if (feedback_target.has_value() &&
8934 feedback_target->map(broker()).is_callable()) {
8935 Node* target_function =
8936 jsgraph()->ConstantNoHole(*feedback_target, broker());
8937 ObjectRef target_ref = feedback_target.value();
8938 if (!target_ref.IsJSFunction()) {
8939 return std::nullopt;
8940 }
8941 JSFunctionRef function = target_ref.AsJSFunction();
8942 SharedFunctionInfoRef shared = function.shared(broker());
8943 Builtin builtin =
8944 shared.HasBuiltinId() ? shared.builtin_id() : Builtin::kNoBuiltinId;
8945 if (builtin == Builtin::kMathMax || builtin == Builtin::kMathMin) {
8946 // Check that the {target} is still the {target_function}.
8947 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
8948 target_function);
8949 effect = graph()->NewNode(
8950 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
8951 effect, control);
8952
8953 // Specialize the JSCallWithArrayLike node to the {target_function}.
8954 NodeProperties::ReplaceValueInput(node, target_function,
8955 n.TargetIndex());
8957 // Try to further reduce the Call MathMin/Max with double array.
8958 return Changed(node).FollowedBy(
8960 }
8961 }
8962 }
8963
8964 return std::nullopt;
8965}
8966
8968 Builtin builtin) {
8970 DCHECK_NE(n.Parameters().speculation_mode(),
8972 DCHECK_EQ(n.ArgumentCount(), 1);
8973
8974 JSCallReducerAssembler a(this, node);
8975 Node* subgraph = a.ReduceJSCallMathMinMaxWithArrayLike(builtin);
8976 return ReplaceWithSubgraph(&a, subgraph);
8977}
8978
8979#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
8980Reduction JSCallReducer::ReduceGetContinuationPreservedEmbedderData(
8981 Node* node) {
8982 JSCallNode n(node);
8983 Effect effect = n.effect();
8984 Control control = n.control();
8985
8986 Node* value = effect = graph()->NewNode(
8987 simplified()->GetContinuationPreservedEmbedderData(), effect);
8988
8989 ReplaceWithValue(node, value, effect, control);
8990 return Replace(node);
8991}
8992
8993Reduction JSCallReducer::ReduceSetContinuationPreservedEmbedderData(
8994 Node* node) {
8995 JSCallNode n(node);
8996 Effect effect = n.effect();
8997 Control control = n.control();
8998
8999 if (n.ArgumentCount() == 0) return NoChange();
9000
9001 effect =
9002 graph()->NewNode(simplified()->SetContinuationPreservedEmbedderData(),
9003 n.Argument(0), effect);
9004
9005 Node* value = jsgraph()->UndefinedConstant();
9006
9007 ReplaceWithValue(node, value, effect, control);
9008 return Replace(node);
9009}
9010#endif // V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
9011
9015
9017
9019
9021
9025
9029
9033
9037
9038} // namespace compiler
9039} // namespace internal
9040} // namespace v8
JSGraph * jsgraph
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype)
#define one
int16_t parameter_count
Definition builtins.cc:67
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
Builtins::Kind kind
Definition builtins.cc:40
size_t size() const
static constexpr int kNumExtraArgsWithReceiver
static constexpr int kNewTargetIndex
static constexpr int kPaddingIndex
static constexpr int kArgcIndex
static constexpr int kTargetIndex
static Address CppEntryOf(Builtin builtin)
Definition builtins.cc:350
static V8_EXPORT_PRIVATE const char * name(Builtin builtin)
Definition builtins.cc:226
static V8_EXPORT_PRIVATE Callable CallableFor(Isolate *isolate, Builtin builtin)
Definition builtins.cc:214
static constexpr BytecodeOffset None()
Definition utils.h:675
Handle< Code > code() const
Definition callable.h:22
CallInterfaceDescriptor descriptor() const
Definition callable.h:23
static const int kMaxArguments
Definition code.h:463
static ExternalReference Create(const SCTableReference &table_ref)
static V8_EXPORT_PRIVATE CompareStringsOptions CompareStringsOptionsFor(IsolateT *isolate, DirectHandle< Object > locales, DirectHandle< Object > options)
v8::internal::Factory * factory()
Definition isolate.h:1527
static const int kMaxCopyElements
Definition js-array.h:127
static constexpr uint32_t kMaxFastArrayLength
Definition js-array.h:136
static constexpr uint32_t kMaxElementIndex
Definition js-objects.h:924
static constexpr MachineRepresentation PointerRepresentation()
constexpr bool IsDouble() const
size_t return_count() const
Definition signature.h:93
base::Vector< const T > all() const
Definition signature.h:117
static constexpr Tagged< Smi > FromEnum(E value)
Definition smi.h:58
static const uint32_t kMaxLength
Definition string.h:511
static TNode UncheckedCast(compiler::Node *node)
Definition tnode.h:413
const_iterator begin() const
void push_back(const T &value)
T * AllocateArray(size_t length)
Definition zone.h:127
static FieldAccess ForJSArrayBufferViewBuffer()
static ElementAccess ForFixedArrayElement()
static FieldAccess ForJSCollectionIteratorTable()
static FieldAccess ForJSArrayBufferViewByteLength()
static FieldAccess ForMap(WriteBarrierKind write_barrier=kMapWriteBarrier)
static FieldAccess ForJSArrayBufferViewByteOffset()
static FieldAccess ForJSArrayIteratorNextIndex()
static FieldAccess ForJSObjectPropertiesOrHashKnownPointer()
static FieldAccess ForJSCollectionIteratorIndex()
static FieldAccess ForOrderedHashMapOrSetNextTable()
static FieldAccess ForJSArrayBufferBitField()
static FieldAccess ForJSObjectInObjectProperty(MapRef map, int index, MachineType machine_type=MachineType::AnyTagged())
static FieldAccess ForJSArrayLength(ElementsKind elements_kind)
static ElementAccess ForOrderedHashMapEntryValue()
static FieldAccess ForJSIteratorResultValue()
static FieldAccess ForOrderedHashMapOrSetNumberOfBuckets()
static FieldAccess ForContextSlot(size_t index)
static FieldAccess ForJSTypedArrayExternalPointer()
static FieldAccess ForJSTypedArrayBasePointer()
static FieldAccess ForJSDataViewDataPointer()
static FieldAccess ForOrderedHashMapOrSetNumberOfDeletedElements()
static FieldAccess ForOrderedHashMapOrSetNumberOfElements()
PropertyAccessInfo FinalizePropertyAccessInfosAsOne(ZoneVector< PropertyAccessInfo > infos, AccessMode access_mode) const
void ReplaceWithValue(Node *node, Node *value, Node *effect=nullptr, Node *control=nullptr)
void MergeControlToEnd(TFGraph *graph, CommonOperatorBuilder *common, Node *node)
static Reduction Replace(Node *node)
void RelaxControls(Node *node, Node *control=nullptr)
void Allocate(int size, AllocationType allocation=AllocationType::kYoung, Type type=Type::Any())
bool CanAllocateArray(int length, MapRef map, AllocationType allocation=AllocationType::kYoung)
void Store(const FieldAccess &access, Node *value)
OptionalHeapObjectRef target() const
ConvertReceiverMode convert_mode() const
CallFeedbackRelation feedback_relation() const
CallFrequency const & frequency() const
FeedbackSource const & feedback() const
SpeculationMode speculation_mode() const
void DependOnStablePrototypeChains(ZoneVector< MapRef > const &receiver_maps, WhereToStart start, OptionalJSObjectRef last_prototype=OptionalJSObjectRef())
FeedbackSource const & feedback() const
CallFrequency const & frequency() const
FeedbackSource const & feedback() const
NameRef GetPropertyKey(JSHeapBroker *broker, InternalIndex descriptor_index) const
static constexpr int ArityForArgc(int c_arg_count, int slow_arg_count)
FastApiCallReducerAssembler(JSCallReducer *reducer, Node *node, const FunctionTemplateInfoRef function_template_info, FastApiCallFunction c_function, Node *receiver, const SharedFunctionInfoRef shared, Node *target, const int arity, Node *effect)
TNode< Object > FastApiCall(CallDescriptor *descriptor, Node **inputs, size_t inputs_size)
IndirectHandle< FeedbackCell > object() const
OptionalFeedbackVectorRef feedback_vector(JSHeapBroker *broker) const
ObjectRef value(JSHeapBroker *broker) const
OptionalObjectRef TryGet(JSHeapBroker *broker, int i) const
const FrameStateInfo & frame_state_info() const
HolderLookupResult LookupHolderOfExpectedType(JSHeapBroker *broker, MapRef receiver_map)
IndirectHandle< FunctionTemplateInfo > object() const
Address callback(JSHeapBroker *broker) const
bool is_signature_undefined(JSHeapBroker *broker) const
OptionalObjectRef callback_data(JSHeapBroker *broker) const
GraphAssemblerLabel< sizeof...(Reps)> * loop_header_label()
detail::GraphAssemblerLabelForReps< Reps... > MakeLabel(Reps... reps)
void BranchWithHint(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *if_true, detail::GraphAssemblerLabelForVars< Vars... > *if_false, BranchHint hint, Vars...)
Node * Checkpoint(FrameState frame_state)
Node * ExternalConstant(ExternalReference ref)
Node * TypeGuard(Type type, Node *value)
void InitializeEffectControl(Node *effect, Node *control)
TNode< Object > Call(const CallDescriptor *call_descriptor, int inputs_size, Node **inputs)
detail::GraphAssemblerLabelForReps< Reps... > MakeDeferredLabel(Reps... reps)
void GotoIf(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *label, BranchHint hint, Vars...)
CommonOperatorBuilder * common() const
void Bind(GraphAssemblerLabel< VarCount > *label)
void GotoIfNot(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *label, BranchHint hint, Vars...)
void Branch(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *if_true, detail::GraphAssemblerLabelForVars< Vars... > *if_false, Vars...)
void Goto(detail::GraphAssemblerLabelForVars< Vars... > *label, Vars...)
V8_EXPORT_PRIVATE MapRef map(JSHeapBroker *broker) const
IteratingArrayBuiltinReducerAssembler(JSCallReducer *reducer, Node *node)
std::pair< TNode< Number >, TNode< Object > > SafeLoadElement(ElementsKind kind, TNode< JSArray > o, TNode< Number > index)
TNode< Number > ReduceArrayPrototypePush(MapInference *inference)
TNode< Object > ReduceArrayPrototypeFind(MapInference *inference, const bool has_stability_dependency, ElementsKind kind, SharedFunctionInfoRef shared, NativeContextRef native_context, ArrayFindVariant variant)
TNode< Smi > LoadJSArrayLength(TNode< JSArray > array, ElementsKind kind)
TNode< FixedArrayBase > LoadElements(TNode< JSObject > o)
TNode< Boolean > ReduceArrayPrototypeEverySome(MapInference *inference, const bool has_stability_dependency, ElementsKind kind, SharedFunctionInfoRef shared, NativeContextRef native_context, ArrayEverySomeVariant variant)
TNode< Object > ReduceArrayPrototypeIndexOfIncludes(ElementsKind kind, ArrayIndexOfIncludesVariant variant)
void StoreFixedArrayBaseElement(TNode< FixedArrayBase > o, TNode< Number > index, TNode< Object > v, ElementsKind kind)
TNode< Object > ReduceArrayPrototypeReduce(MapInference *inference, const bool has_stability_dependency, ElementsKind kind, ArrayReduceDirection direction, SharedFunctionInfoRef shared)
TNode< Object > MaybeSkipHole(TNode< Object > o, ElementsKind kind, GraphAssemblerLabel< sizeof...(Vars)> *continue_label, TNode< Vars >... vars)
TNode< Boolean > HoleCheck(ElementsKind kind, TNode< Object > v)
void StoreJSArrayLength(TNode< JSArray > array, TNode< Number > value, ElementsKind kind)
TNode< Object > ReduceArrayPrototypeAt(ZoneVector< MapRef > kinds, bool needs_fallback_builtin_call)
TNode< JSArray > ReduceArrayPrototypeFilter(MapInference *inference, const bool has_stability_dependency, ElementsKind kind, SharedFunctionInfoRef shared, NativeContextRef native_context)
TNode< Object > ReduceArrayPrototypeForEach(MapInference *inference, const bool has_stability_dependency, ElementsKind kind, SharedFunctionInfoRef shared)
TNode< JSArray > ReduceArrayPrototypeMap(MapInference *inference, const bool has_stability_dependency, ElementsKind kind, SharedFunctionInfoRef shared, NativeContextRef native_context)
TNode< Smi > LoadFixedArrayBaseLength(TNode< FixedArrayBase > o)
ObjectRef GetBoilerplateLength(JSHeapBroker *broker) const
const CallParameters & Parameters() const
static constexpr int ArityForArgc(int parameters)
static constexpr int FeedbackVectorIndexForArgc(int argc)
static constexpr int ArgumentIndex(int i)
TNode< Object > ArgumentOrUndefined(int i, JSGraph *jsgraph) const
static constexpr MachineRepresentation kPhiRepresentation
ForBuilder0(JSGraphAssembler *gasm, TNode< Number > initial_value, const ConditionFunction1 &cond, const StepFunction1 &step)
static constexpr MachineRepresentation kPhiRepresentation
V8_WARN_UNUSED_RESULT ForBuilder1 & Do(const For1BodyFunction &body)
ForBuilder1(JSGraphAssembler *gasm, TNode< Number > initial_value, const ConditionFunction1 &cond, const StepFunction1 &step, TNode< Object > initial_arg0)
TryCatchBuilder0(JSCallReducerAssembler *gasm, const TryFunction &try_body)
TNode< Smi > TypeGuardUnsignedSmall(TNode< Object > value)
TNode< JSArray > CreateArrayNoThrow(TNode< Object > ctor, TNode< Number > size, FrameState frame_state)
TNode< Number > CheckBounds(TNode< Number > value, TNode< Number > limit, CheckBoundsFlags flags={})
TNode< Object > ReduceMathBinary(const Operator *op)
TNode< Object > TypeGuardNonInternal(TNode< Object > value)
TNode< Number > CheckNumber(TNode< Object > value)
JSCallReducerAssembler(JSCallReducer *reducer, Node *node, Node *effect=nullptr, Node *control=nullptr)
ForBuilder1 For1ZeroUntil(TNode< Number > excluded_limit, TNode< Object > initial_arg0)
TNode< Number > LoadMapElementsKind(TNode< Map > map)
std::function< TNode< Number >(TNode< Number >)> StepFunction1
TryCatchBuilder0 Try(const VoidGenerator0 &try_body)
TNode< Number > SpeculativeToNumber(TNode< Object > value, NumberOperationHint hint=NumberOperationHint::kNumberOrOddball)
std::function< TNode< Boolean >(TNode< Number >)> ConditionFunction1
std::function< void(TNode< Number >, TNode< Object > *)> For1BodyFunction
ForBuilder0 ForZeroUntil(TNode< Number > excluded_limit)
TNode< Object > ConvertHoleToUndefined(TNode< Object > value, ElementsKind kind)
TNode< Object > JSCall3(TNode< Object > function, TNode< Object > this_arg, TNode< Object > arg0, TNode< Object > arg1, TNode< Object > arg2, FrameState frame_state)
TNode< Object > Call4(const Callable &callable, TNode< Context > context, TNode< Object > arg0, TNode< Object > arg1, TNode< Object > arg2, TNode< Object > arg3)
TNode< JSArray > AllocateEmptyJSArray(ElementsKind kind, NativeContextRef native_context)
TNode< Object > ReduceMathUnary(const Operator *op)
TNode< String > CheckString(TNode< Object > value)
TNode< T > EnterMachineGraph(TNode< U > input, UseInfo use_info)
ForBuilder0 Forever(TNode< Number > initial_value, const StepFunction1 &step)
void ThrowIfNotCallable(TNode< Object > maybe_callable, FrameState frame_state)
TNode< Number > NumberInc(TNode< Number > value)
TNode< Number > TypeGuardFixedArrayLength(TNode< Object > value)
TNode< Object > JSCall4(TNode< Object > function, TNode< Object > this_arg, TNode< Object > arg0, TNode< Object > arg1, TNode< Object > arg2, TNode< Object > arg3, FrameState frame_state)
TNode< Object > ReduceJSCallWithArrayLikeOrSpreadOfEmpty(std::unordered_set< Node * > *generated_calls_with_array_like_or_spread)
TNode< Object > Argument(int index) const
CompilationDependencies * dependencies() const
TNode< Object > ReduceJSCallMathMinMaxWithArrayLike(Builtin builtin)
ForBuilder1 For1(TNode< Number > initial_value, const ConditionFunction1 &cond, const StepFunction1 &step, TNode< Object > initial_arg0)
TNode< T > ExitMachineGraph(TNode< U > input, MachineRepresentation output_representation, Type output_type)
void MaybeInsertMapChecks(MapInference *inference, bool has_stability_dependency)
TNode< Smi > CheckSmi(TNode< Object > value)
Reduction ReduceDatePrototypeGetTime(Node *node)
Reduction ReduceArrayMap(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceStringPrototypeStringAt(const Operator *string_access_operator, Node *node)
bool IsBuiltinOrApiFunction(JSFunctionRef target_ref) const
Reduction ReduceTypedArrayPrototypeToStringTag(Node *node)
Reduction ReduceArrayFindIndex(Node *node, SharedFunctionInfoRef shared)
bool DoPromiseChecks(MapInference *inference)
Reduction ReduceStringPrototypeEndsWith(Node *node)
Reduction ReduceObjectPrototypeIsPrototypeOf(Node *node)
Reduction ReplaceWithSubgraph(JSCallReducerAssembler *gasm, Node *subgraph)
Reduction ReduceArrayEvery(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceFunctionPrototypeHasInstance(Node *node)
Reduction ReduceCallApiFunction(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceMathBinary(Node *node, const Operator *op)
Reduction ReduceRegExpPrototypeTest(Node *node)
Reduction ReduceArrayReduceRight(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceMathUnary(Node *node, const Operator *op)
Node * ConvertHoleToUndefined(Node *value, ElementsKind elements_kind)
Reduction ReduceArrayReduce(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceArrayPrototypeShift(Node *node)
Reduction ReduceStringPrototypeStartsWith(Node *node)
Node * CreateClosureFromBuiltinSharedFunctionInfo(SharedFunctionInfoRef shared, Node *context, Node *effect, Node *control)
Reduction ReduceArrayIterator(Node *node, ArrayIteratorKind array_kind, IterationKind iteration_kind)
Reduction ReduceFunctionPrototypeApply(Node *node)
Reduction ReduceStringPrototypeConcat(Node *node)
Node * LoadReceiverElementsKind(Node *receiver, Effect *effect, Control control)
Reduction ReduceMathMinMax(Node *node, const Operator *op, Node *empty_value)
Reduction ReduceStringPrototypeCharAt(Node *node)
Reduction ReduceStringPrototypeSubstr(Node *node)
Reduction ReduceJSCallWithArrayLike(Node *node)
Reduction ReduceArrayFind(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceBigIntAsN(Node *node, Builtin builtin)
Reduction ReducePromiseResolveTrampoline(Node *node)
Reduction ReduceCollectionIteration(Node *node, CollectionKind collection_kind, IterationKind iteration_kind)
Reduction ReduceObjectPrototypeGetProto(Node *node)
Reduction ReducePromisePrototypeFinally(Node *node)
Reduction ReduceArrayIteratorPrototypeNext(Node *node)
JSOperatorBuilder * javascript() const
Reduction ReduceObjectGetPrototype(Node *node, Node *object)
Reduction ReduceFunctionPrototypeBind(Node *node)
Reduction ReduceStringIteratorPrototypeNext(Node *node)
Reduction ReduceJSConstructForwardAllArgs(Node *node)
Reduction ReducePromisePrototypeThen(Node *node)
Reduction ReduceCallOrConstructWithArrayLikeOrSpread(Node *node, int argument_count, int arraylike_or_spread_index, CallFrequency const &frequency, FeedbackSource const &feedback_source, SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation, Node *target, Effect effect, Control control)
Reduction ReduceReflectGetPrototypeOf(Node *node)
Reduction ReduceObjectPrototypeHasOwnProperty(Node *node)
std::optional< Reduction > TryReduceJSCallMathMinMaxWithArrayLike(Node *node)
Reduction ReduceStringPrototypeIndexOfIncludes(Node *node, StringIndexOfIncludesVariant variant)
Reduction ReduceStringPrototypeSubstring(Node *node)
Node * CheckArrayLength(Node *array, ElementsKind elements_kind, uint32_t array_length, const FeedbackSource &feedback_source, Effect effect, Control control)
CompilationDependencies * dependencies() const
Reduction ReduceCallWasmFunction(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceArrayBufferViewAccessor(Node *node, InstanceType instance_type, FieldAccess const &access, Builtin builtin)
Reduction ReduceJSCallMathMinMaxWithArrayLike(Node *node, Builtin builtin)
Reduction ReduceFunctionPrototypeCall(Node *node)
Reduction ReduceCollectionPrototypeSize(Node *node, CollectionKind collection_kind)
Reduction ReduceStringConstructor(Node *node, JSFunctionRef constructor)
void CheckIfElementsKind(Node *receiver_elements_kind, ElementsKind kind, Node *control, Node **if_true, Node **if_false)
Reduction ReduceArraySome(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceJSConstructWithArrayLike(Node *node)
Reduction ReduceForInsufficientFeedback(Node *node, DeoptimizeReason reason)
SimplifiedOperatorBuilder * simplified() const
Reduction ReducePromisePrototypeCatch(Node *node)
std::pair< Node *, Node * > ReleaseEffectAndControlFromAssembler(JSCallReducerAssembler *gasm)
CommonOperatorBuilder * common() const
static constexpr int kMaxInlineMatchSequence
Reduction ReduceArrayBufferViewByteOffsetAccessor(Node *node, InstanceType instance_type, Builtin builtin)
Reduction ReduceNumberIsSafeInteger(Node *node)
Reduction ReduceJSConstructWithSpread(Node *node)
Reduction ReduceStringPrototypeIterator(Node *node)
Reduction ReduceStringPrototypeSlice(Node *node)
Reduction ReduceCallOrConstructWithArrayLikeOrSpreadOfCreateArguments(Node *node, Node *arguments_list, int arraylike_or_spread_index, CallFrequency const &frequency, FeedbackSource const &feedback, SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation)
Reduction ReduceObjectGetPrototypeOf(Node *node)
Reduction ReduceArrayBufferViewByteLengthAccessor(Node *node, InstanceType instance_type, Builtin builtin)
std::unordered_set< Node * > generated_calls_with_array_like_or_spread_
Reduction ReduceDataViewAccess(Node *node, DataViewAccess access, ExternalArrayType element_type)
Reduction ReduceCollectionIteratorPrototypeNext(Node *node, int entry_size, Handle< HeapObject > empty_collection, InstanceType collection_iterator_instance_type_first, InstanceType collection_iterator_instance_type_last)
Reduction ReduceTypedArrayPrototypeLength(Node *node)
Reduction ReduceArrayPrototypeSlice(Node *node)
Reduction ReduceStringFromCodePoint(Node *node)
Reduction ReduceArrayFilter(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceArrayForEach(Node *node, SharedFunctionInfoRef shared)
Reduction ReduceCollectionPrototypeHas(Node *node, CollectionKind collection_kind)
Reduction ReduceTypedArrayConstructor(Node *node, SharedFunctionInfoRef shared)
const CreateClosureParameters & Parameters() const
const CreateLiteralParameters & Parameters() const
void MergeExceptionalPaths(TNode< Object > *exception_out, Effect *effect_out, Control *control_out)
IfBuilder0 & Then(const VoidGenerator0 &body)
TNode< Number > StringLength(TNode< String > string)
TNode< Map > LoadMap(TNode< HeapObject > object)
TNode< Number > NumberConstant(double value)
Node * StoreField(FieldAccess const &, Node *object, Node *value)
TNode< Object > ConvertTaggedHoleToUndefined(TNode< Object > value)
Node * StringCharCodeAt(TNode< String > string, TNode< Number > position)
TNode< Number > NumberMax(TNode< Number > lhs, TNode< Number > rhs)
TNode< Object > DoubleArrayMin(TNode< JSArray > array)
Node * StoreElement(ElementAccess const &, Node *object, Node *index, Node *value)
TNode< HeapObject > HeapConstant(Handle< HeapObject > object)
TNode< Object > JSCallRuntime2(Runtime::FunctionId function_id, TNode< Object > arg0, TNode< Object > arg1, TNode< Context > context, FrameState frame_state)
TNode< Object > MayThrow(const NodeGenerator0 &body)
TNode< FixedArrayBase > MaybeGrowFastElements(ElementsKind kind, const FeedbackSource &feedback, TNode< JSArray > array, TNode< FixedArrayBase > elements, TNode< Number > new_length, TNode< Number > old_length)
TNode< Boolean > NumberLessThan(TNode< Number > lhs, TNode< Number > rhs)
TNode< Object > Constant(ObjectRef ref)
TNode< Number > NumberBitwiseAnd(TNode< Number > lhs, TNode< Number > rhs)
TNode< Boolean > ToBoolean(TNode< Object > value)
TNode< Number > NumberShiftRightLogical(TNode< Number > lhs, TNode< Number > rhs)
IfBuilder1< T, Boolean > SelectIf(TNode< Boolean > cond)
Node * CheckIf(Node *cond, DeoptimizeReason reason, const FeedbackSource &feedback={})
TNode< Number > NumberSubtract(TNode< Number > lhs, TNode< Number > rhs)
TNode< Boolean > ReferenceEqual(TNode< Object > lhs, TNode< Object > rhs)
TNode< Object > JSCallRuntime1(Runtime::FunctionId function_id, TNode< Object > arg0, TNode< Context > context, std::optional< FrameState > frame_state, Operator::Properties properties=Operator::kNoProperties)
void TransitionAndStoreElement(MapRef double_map, MapRef fast_map, TNode< HeapObject > object, TNode< Number > index, TNode< Object > value)
TNode< Number > NumberAdd(TNode< Number > lhs, TNode< Number > rhs)
TNode< Number > NumberMin(TNode< Number > lhs, TNode< Number > rhs)
IfBuilder0 IfNot(TNode< Boolean > cond)
TNode< Boolean > ObjectIsSmi(TNode< Object > value)
TNode< Boolean > NumberLessThanOrEqual(TNode< Number > lhs, TNode< Number > rhs)
TNode< String > StringSubstring(TNode< String > string, TNode< Number > from, TNode< Number > to)
Node * LoadField(FieldAccess const &, Node *object)
TNode< Boolean > NumberEqual(TNode< Number > lhs, TNode< Number > rhs)
TNode< String > StringFromSingleCharCode(TNode< Number > code)
TNode< Smi > SmiConstant(int32_t value)
TNode< Object > DoubleArrayMax(TNode< JSArray > array)
SimplifiedOperatorBuilder * simplified() override
TNode< Boolean > NumberIsFloat64Hole(TNode< Number > value)
TNode< Boolean > ObjectIsCallable(TNode< Object > value)
Node * LoadElement(ElementAccess const &, Node *object, Node *index)
JSOperatorBuilder * javascript() const
Definition js-graph.h:104
SimplifiedOperatorBuilder * simplified() const
Definition js-graph.h:105
Node * HeapConstantNoHole(Handle< HeapObject > value)
Definition js-graph.cc:146
Node * CEntryStubConstant(int result_size, ArgvMode argv_mode=ArgvMode::kStack, bool builtin_exit_frame=false)
Definition js-graph.cc:23
Isolate * isolate() const
Definition js-graph.h:106
Node * ConstantNoHole(ObjectRef ref, JSHeapBroker *broker)
Definition js-graph.cc:51
Handle< T > CanonicalPersistentHandle(Tagged< T > object)
CompilationDependencies * dependencies() const
ProcessedFeedback const & GetFeedbackForArrayOrObjectLiteral(FeedbackSource const &source)
ProcessedFeedback const & GetFeedbackForCall(FeedbackSource const &source)
NativeContextRef target_native_context() const
const Operator * CallForwardVarargs(size_t arity, uint32_t start_index)
const Operator * Call(size_t arity, CallFrequency const &frequency=CallFrequency(), FeedbackSource const &feedback=FeedbackSource(), ConvertReceiverMode convert_mode=ConvertReceiverMode::kAny, SpeculationMode speculation_mode=SpeculationMode::kDisallowSpeculation, CallFeedbackRelation feedback_relation=CallFeedbackRelation::kUnrelated)
const Operator * ConstructForwardVarargs(size_t arity, uint32_t start_index)
static CallDescriptor * GetStubCallDescriptor(Zone *zone, const CallInterfaceDescriptor &descriptor, int stack_parameter_count, CallDescriptor::Flags flags, Operator::Properties properties=Operator::kNoProperties, StubCallMode stub_mode=StubCallMode::kCallCodeObject)
Definition linkage.cc:587
static CallDescriptor * GetCEntryStubCallDescriptor(Zone *zone, int return_count, int js_parameter_count, const char *debug_name, Operator::Properties properties, CallDescriptor::Flags flags, StackArgumentOrder stack_order=StackArgumentOrder::kDefault)
Definition linkage.cc:447
Node * ExternalConstant(ExternalReference ref)
CommonOperatorBuilder * common() const
V8_WARN_UNUSED_RESULT bool Is(MapRef expected_map)
bool RelyOnMapsPreferStability(CompilationDependencies *dependencies, JSGraph *jsgraph, Effect *effect, Control control, const FeedbackSource &feedback)
V8_WARN_UNUSED_RESULT bool RelyOnMapsViaStability(CompilationDependencies *dependencies)
V8_WARN_UNUSED_RESULT ZoneRefSet< Map > const & GetMaps()
V8_WARN_UNUSED_RESULT bool AllOfInstanceTypesAre(InstanceType type) const
V8_WARN_UNUSED_RESULT Reduction NoChange()
void InsertMapChecks(JSGraph *jsgraph, Effect *effect, Control control, const FeedbackSource &feedback)
V8_WARN_UNUSED_RESULT bool HaveMaps() const
V8_WARN_UNUSED_RESULT bool AllOfInstanceTypesAreJSReceiver() const
DescriptorArrayRef instance_descriptors(JSHeapBroker *broker) const
HeapObjectRef prototype(JSHeapBroker *broker) const
InstanceType instance_type() const
ElementsKind elements_kind() const
bool supports_fast_array_iteration(JSHeapBroker *broker) const
IndirectHandle< Name > object() const
static void ChangeOp(Node *node, const Operator *new_op)
static void ReplaceEffectInput(Node *node, Node *effect, int index=0)
static void ReplaceControlInput(Node *node, Node *control, int index=0)
static bool CanBeNullOrUndefined(JSHeapBroker *broker, Node *receiver, Effect effect)
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetContextInput(Node *node)
static void ReplaceContextInput(Node *node, Node *context)
static Node * GetFrameStateInput(Node *node)
static bool CanBePrimitive(JSHeapBroker *broker, Node *receiver, Effect effect)
static Node * GetValueInput(Node *node, int index)
static void SetType(Node *node, Type type)
static void ReplaceValueInput(Node *node, Node *value, int index)
static Node * FindFrameStateBefore(Node *node, Node *unreachable_sentinel)
static void ReplaceValueInputs(Node *node, Node *value)
static void ReplaceFrameStateInput(Node *node, Node *frame_state)
static bool NoObservableSideEffectBetween(Node *effect, Node *dominator)
static bool IsExceptionalCall(Node *node, Node **out_exception=nullptr)
static Node * GetControlInput(Node *node, int index=0)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
const Operator * op() const
Definition node.h:50
void ReplaceInput(int index, Node *new_to)
Definition node.h:76
Node * InputAt(int index) const
Definition node.h:70
void InsertInput(Zone *zone, int index, Node *new_to)
Definition node.cc:203
constexpr Opcode opcode() const
Definition operator.h:75
static OutputFrameStateCombine Ignore()
TNode< Context > CreateFunctionContext(NativeContextRef native_context, TNode< Context > outer_context, int slot_count)
void StoreContextSlot(TNode< Context > context, size_t slot_index, TNode< Object > value)
TNode< Object > ReducePromiseConstructor(NativeContextRef native_context)
void CallPromiseReject(TNode< JSFunction > reject, TNode< Object > exception, FrameState frame_state)
TNode< JSFunction > CreateClosureFromBuiltinSharedFunctionInfo(SharedFunctionInfoRef shared, TNode< Context > context)
void CallPromiseExecutor(TNode< Object > executor, TNode< JSFunction > resolve, TNode< JSFunction > reject, FrameState frame_state)
TNode< JSPromise > CreatePromise(TNode< Context > context)
PromiseBuiltinReducerAssembler(JSCallReducer *reducer, Node *node)
Representation field_representation() const
OptionalJSObjectRef holder() const
ZoneVector< MapRef > const & lookup_start_object_maps() const
static Reduction Changed(Node *node)
Reduction FollowedBy(Reduction next) const
bool HasBreakInfo(JSHeapBroker *broker) const
std::optional< double > ToInt(JSHeapBroker *broker, int radix)
std::optional< uint16_t > GetChar(JSHeapBroker *broker, uint32_t index) const
Node * CloneNode(const Node *node)
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
static TypeCache const * Get()
#define V8_INFINITY
Definition globals.h:23
int start
int end
#define RAB_GSAB_TYPED_ARRAYS(V)
#define TYPED_ARRAYS(V)
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
DirectHandle< Object > new_target
Definition execution.cc:75
JSHeapBroker * broker
int32_t offset
TNode< Object > original_length
TNode< Context > context
bool can_reduce_
TNode< Object > target
Node * node_ptr
std::optional< TNode< JSArray > > a
Control control_
Node * receiver_
TNode< Object > this_arg
MapInference inference_
TNode< Object > receiver
Effect effect_
ArrayReduceDirection direction
SharedFunctionInfoRef shared
FrameState outer_frame_state
bool has_stability_dependency_
TNode< Object > callback
#define _
ElementsKind elements_kind_
#define TRACE_BROKER_MISSING(broker, x)
ZoneVector< RpoNumber > & result
MovableLabel continuation
Point to
int position
Definition liveedit.cc:290
int m
Definition mul-fft.cc:294
int n
Definition mul-fft.cc:296
bool none_of(const C &container, const P &predicate)
FastApiCallFunction GetFastApiCallTarget(JSHeapBroker *broker, FunctionTemplateInfoRef function_template_info, size_t arg_count)
TNode< Oddball > UndefinedConstant(JSGraph *jsgraph)
const CreateArrayIteratorParameters & CreateArrayIteratorParametersOf(const Operator *op)
JSCallNodeBase< IrOpcode::kJSCall > JSCallNode
NumberConstant(std::numeric_limits< double >::quiet_NaN())) DEFINE_GETTER(EmptyStateValues
FrameState CreateGenericLazyDeoptContinuationFrameState(JSGraph *graph, SharedFunctionInfoRef shared, Node *target, Node *context, Node *receiver, Node *outer_frame_state)
const CreateBoundFunctionParameters & CreateBoundFunctionParametersOf(const Operator *op)
const FieldAccess & FieldAccessOf(const Operator *op)
FloatMatcher< double, IrOpcode::kNumberConstant > NumberMatcher
const CallParameters & CallParametersOf(const Operator *op)
Handle< FeedbackCell > FeedbackCellOf(const Operator *op)
HeapConstantNoHole(BUILTIN_CODE(isolate(), AllocateInOldGeneration))) DEFINE_GETTER(ArrayConstructorStubConstant
Node * CreateInlinedApiFunctionFrameState(JSGraph *graph, SharedFunctionInfoRef shared, Node *target, Node *context, Node *receiver, Node *outer_frame_state)
FrameState CreateJavaScriptBuiltinContinuationFrameState(JSGraph *jsgraph, SharedFunctionInfoRef shared, Builtin name, Node *target, Node *context, Node *const *stack_parameters, int stack_parameter_count, Node *outer_frame_state, ContinuationFrameStateMode mode)
int ExternalArrayElementSize(const ExternalArrayType element_type)
Definition globals.h:156
std::string ToString(const BytecodeLivenessState &liveness)
CreateArgumentsType const & CreateArgumentsTypeOf(const Operator *op)
ref_traits< T >::ref_type MakeRef(JSHeapBroker *broker, Tagged< T > object)
ZoneCompactSet< typename ref_traits< T >::ref_type > ZoneRefSet
Definition heap-refs.h:1301
HeapObjectMatcherImpl< IrOpcode::kHeapConstant > HeapObjectMatcher
constexpr IndependentValueType kWasmF32
constexpr IndependentHeapType kWasmExternRef
constexpr IndependentValueType kWasmI32
constexpr IndependentValueType kWasmF64
constexpr IndependentValueType kWasmI64
constexpr bool IsHoleyElementsKind(ElementsKind kind)
bool IsClassConstructor(FunctionKind kind)
bool Is(IndirectHandle< U > value)
Definition handles-inl.h:51
bool IsSpecialReceiverInstanceType(InstanceType instance_type)
Map::Bits1::HasPrototypeSlotBit Map::Bits1::HasNamedInterceptorBit Map::Bits1::IsUndetectableBit Map::Bits1::IsConstructorBit bit_field2
Definition map-inl.h:123
bool IsTypedArrayElementsKind(ElementsKind kind)
bool IsRabGsabTypedArrayElementsKind(ElementsKind kind)
Tagged(T object) -> Tagged< T >
constexpr bool IsSmiElementsKind(ElementsKind kind)
kWasmInternalFunctionIndirectPointerTag instance_data
ElementsKind GetPackedElementsKind(ElementsKind holey_kind)
constexpr bool IsFloat16TypedArrayElementsKind(ElementsKind kind)
@ FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND
@ LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND
@ FIRST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND
bool IsSmiOrObjectElementsKind(ElementsKind kind)
void Terminate(Isolate *isolate)
Definition bigint.cc:1187
ElementsKind GetHoleyElementsKind(ElementsKind packed_kind)
DONT_OVERRIDE DISABLE_ALLOCATION_SITES HOLEY_ELEMENTS
uint32_t NumberToUint32(Tagged< Object > number)
DONT_OVERRIDE DISABLE_ALLOCATION_SITES DISABLE_ALLOCATION_SITES HOLEY_DOUBLE_ELEMENTS
int32_t DoubleToInt32(double x)
bool UnionElementsKindUptoSize(ElementsKind *a_out, ElementsKind b)
V8_EXPORT_PRIVATE FlagValues v8_flags
constexpr int JSParameterCount(int param_count_without_receiver)
Definition globals.h:2782
@ kExternalFloat64Array
Definition globals.h:2461
@ kExternalUint32Array
Definition globals.h:2458
@ kExternalBigInt64Array
Definition globals.h:2463
@ kExternalInt32Array
Definition globals.h:2457
@ kExternalInt8Array
Definition globals.h:2453
@ kExternalUint8Array
Definition globals.h:2454
@ kExternalUint16Array
Definition globals.h:2456
@ kExternalFloat32Array
Definition globals.h:2460
@ kExternalInt16Array
Definition globals.h:2455
@ kExternalFloat16Array
Definition globals.h:2459
@ kExternalBigUint64Array
Definition globals.h:2464
return value
Definition map-inl.h:893
const int kSmiMaxValue
constexpr bool Is64()
constexpr bool IsDoubleElementsKind(ElementsKind kind)
@ kStartAtPrototype
Definition globals.h:1714
bool UnionElementsKindUptoPackedness(ElementsKind *a_out, ElementsKind b)
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
uint32_t equals
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK_GE(lhs, rhs)
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293
#define arraysize(array)
Definition macros.h:67
bool IsInRange(const T &low, const T &high) const
bool Is(IndirectHandle< HeapObject > const &value) const
HeapObjectRef Ref(JSHeapBroker *broker) const
CallOptimization::HolderLookup lookup
Definition heap-refs.h:951
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671