@@ -3106,6 +3106,8 @@ void BytecodeGenerator::BuildCreateArrayLiteral(
31063106 .StoreAccumulatorInRegister (index);
31073107 }
31083108 } else {
3109+ // TODO(v8:11582): Support allocating boilerplates here.
3110+
31093111 // In other cases, we prepare an empty array to be filled in below.
31103112 DCHECK (!elements->is_empty ());
31113113 int literal_index = feedback_index (feedback_spec ()->AddLiteralSlot ());
@@ -5022,17 +5024,30 @@ void BytecodeGenerator::VisitCall(Call* expr) {
50225024 return VisitCallSuper (expr);
50235025 }
50245026
5027+ // We compile the call differently depending on the presence of spreads and
5028+ // their positions.
5029+ //
5030+ // If there is only one spread and it is the final argument, there is a
5031+ // special CallWithSpread bytecode.
5032+ //
5033+ // If there is a non-final spread, we rewrite calls like
5034+ // callee(1, ...x, 2)
5035+ // to
5036+ // %reflect_apply(callee, receiver, [1, ...x, 2])
5037+ const Call::SpreadPosition spread_position = expr->spread_position ();
5038+
50255039 // Grow the args list as we visit receiver / arguments to avoid allocating all
50265040 // the registers up-front. Otherwise these registers are unavailable during
50275041 // receiver / argument visiting and we can end up with memory leaks due to
50285042 // registers keeping objects alive.
5029- Register callee = register_allocator ()->NewRegister ();
50305043 RegisterList args = register_allocator ()->NewGrowableRegisterList ();
50315044
5045+ // The callee is the first register in args for ease of calling %reflect_apply
5046+ // if we have a non-final spread. For all other cases it is popped from args
5047+ // before emitting the call below.
5048+ Register callee = register_allocator ()->GrowRegisterList (&args);
5049+
50325050 bool implicit_undefined_receiver = false ;
5033- // When a call contains a spread, a Call AST node is only created if there is
5034- // exactly one spread, and it is the last argument.
5035- bool is_spread_call = expr->only_last_arg_is_spread ();
50365051 bool optimize_as_one_shot = ShouldOptimizeAsOneShot ();
50375052
50385053 // TODO(petermarshall): We have a lot of call bytecodes that are very similar,
@@ -5052,7 +5067,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
50525067 }
50535068 case Call::GLOBAL_CALL: {
50545069 // Receiver is undefined for global calls.
5055- if (!is_spread_call && !optimize_as_one_shot) {
5070+ if (spread_position == Call:: kNoSpread && !optimize_as_one_shot) {
50565071 implicit_undefined_receiver = true ;
50575072 } else {
50585073 // TODO(leszeks): There's no special bytecode for tail calls or spread
@@ -5088,7 +5103,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
50885103 }
50895104 case Call::OTHER_CALL: {
50905105 // Receiver is undefined for other calls.
5091- if (!is_spread_call && !optimize_as_one_shot) {
5106+ if (spread_position == Call:: kNoSpread && !optimize_as_one_shot) {
50925107 implicit_undefined_receiver = true ;
50935108 } else {
50945109 // TODO(leszeks): There's no special bytecode for tail calls or spread
@@ -5137,25 +5152,51 @@ void BytecodeGenerator::VisitCall(Call* expr) {
51375152 BuildIncrementBlockCoverageCounterIfEnabled (right_range);
51385153 }
51395154
5140- // Evaluate all arguments to the function call and store in sequential args
5141- // registers.
5142- VisitArguments (expr->arguments (), &args);
5143- int receiver_arg_count = implicit_undefined_receiver ? 0 : 1 ;
5144- CHECK_EQ (receiver_arg_count + expr->arguments ()->length (),
5145- args.register_count ());
5155+ int receiver_arg_count = -1 ;
5156+ if (spread_position == Call::kHasNonFinalSpread ) {
5157+ // If we're building %reflect_apply, build the array literal and put it in
5158+ // the 3rd argument.
5159+ DCHECK (!implicit_undefined_receiver);
5160+ DCHECK_EQ (args.register_count (), 2 );
5161+ BuildCreateArrayLiteral (expr->arguments (), nullptr );
5162+ builder ()->StoreAccumulatorInRegister (
5163+ register_allocator ()->GrowRegisterList (&args));
5164+ } else {
5165+ // If we're not building %reflect_apply and don't need to build an array
5166+ // literal, pop the callee and evaluate all arguments to the function call
5167+ // and store in sequential args registers.
5168+ args = args.PopLeft ();
5169+ VisitArguments (expr->arguments (), &args);
5170+ receiver_arg_count = implicit_undefined_receiver ? 0 : 1 ;
5171+ CHECK_EQ (receiver_arg_count + expr->arguments ()->length (),
5172+ args.register_count ());
5173+ }
51465174
51475175 // Resolve callee for a potential direct eval call. This block will mutate the
51485176 // callee value.
51495177 if (expr->is_possibly_eval () && expr->arguments ()->length () > 0 ) {
51505178 RegisterAllocationScope inner_register_scope (this );
5179+ RegisterList runtime_call_args = register_allocator ()->NewRegisterList (6 );
51515180 // Set up arguments for ResolvePossiblyDirectEval by copying callee, source
51525181 // strings and function closure, and loading language and
51535182 // position.
5154- Register first_arg = args[receiver_arg_count];
5155- RegisterList runtime_call_args = register_allocator ()->NewRegisterList (6 );
5183+
5184+ // Move the first arg.
5185+ if (spread_position == Call::kHasNonFinalSpread ) {
5186+ int feedback_slot_index =
5187+ feedback_index (feedback_spec ()->AddKeyedLoadICSlot ());
5188+ Register args_array = args[2 ];
5189+ builder ()
5190+ ->LoadLiteral (Smi::FromInt (0 ))
5191+ .LoadKeyedProperty (args_array, feedback_slot_index)
5192+ .StoreAccumulatorInRegister (runtime_call_args[1 ]);
5193+ } else {
5194+ // FIXME(v8:5690): Support final spreads for eval.
5195+ DCHECK_GE (receiver_arg_count, 0 );
5196+ builder ()->MoveRegister (args[receiver_arg_count], runtime_call_args[1 ]);
5197+ }
51565198 builder ()
51575199 ->MoveRegister (callee, runtime_call_args[0 ])
5158- .MoveRegister (first_arg, runtime_call_args[1 ])
51595200 .MoveRegister (Register::function_closure (), runtime_call_args[2 ])
51605201 .LoadLiteral (Smi::FromEnum (language_mode ()))
51615202 .StoreAccumulatorInRegister (runtime_call_args[3 ])
@@ -5172,10 +5213,12 @@ void BytecodeGenerator::VisitCall(Call* expr) {
51725213
51735214 builder ()->SetExpressionPosition (expr);
51745215
5175- if (is_spread_call ) {
5216+ if (spread_position == Call:: kHasFinalSpread ) {
51765217 DCHECK (!implicit_undefined_receiver);
51775218 builder ()->CallWithSpread (callee, args,
51785219 feedback_index (feedback_spec ()->AddCallICSlot ()));
5220+ } else if (spread_position == Call::kHasNonFinalSpread ) {
5221+ builder ()->CallJSRuntime (Context::REFLECT_APPLY_INDEX, args);
51795222 } else if (optimize_as_one_shot) {
51805223 DCHECK (!implicit_undefined_receiver);
51815224 builder ()->CallNoFeedback (callee, args);
@@ -5198,10 +5241,20 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
51985241 SuperCallReference* super = expr->expression ()->AsSuperCallReference ();
51995242 const ZonePtrList<Expression>* args = expr->arguments ();
52005243
5201- int first_spread_index = 0 ;
5202- for (; first_spread_index < args->length (); first_spread_index++) {
5203- if (args->at (first_spread_index)->IsSpread ()) break ;
5204- }
5244+ // We compile the super call differently depending on the presence of spreads
5245+ // and their positions.
5246+ //
5247+ // If there is only one spread and it is the final argument, there is a
5248+ // special ConstructWithSpread bytecode.
5249+ //
5250+ // It there is a non-final spread, we rewrite something like
5251+ // super(1, ...x, 2)
5252+ // to
5253+ // %reflect_construct(constructor, [1, ...x, 2], new_target)
5254+ //
5255+ // That is, we implement (non-last-arg) spreads in super calls via our
5256+ // mechanism for spreads in array literals.
5257+ const Call::SpreadPosition spread_position = expr->spread_position ();
52055258
52065259 // Prepare the constructor to the super call.
52075260 Register this_function = VisitForRegisterValue (super->this_function_var ());
@@ -5210,14 +5263,7 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
52105263 ->LoadAccumulatorWithRegister (this_function)
52115264 .GetSuperConstructor (constructor);
52125265
5213- if (first_spread_index < expr->arguments ()->length () - 1 ) {
5214- // We rewrite something like
5215- // super(1, ...x, 2)
5216- // to
5217- // %reflect_construct(constructor, [1, ...x, 2], new_target)
5218- // That is, we implement (non-last-arg) spreads in super calls via our
5219- // mechanism for spreads in array literals.
5220-
5266+ if (spread_position == Call::kHasNonFinalSpread ) {
52215267 // First generate the array containing all arguments.
52225268 BuildCreateArrayLiteral (args, nullptr );
52235269
@@ -5244,11 +5290,11 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
52445290
52455291 int feedback_slot_index = feedback_index (feedback_spec ()->AddCallICSlot ());
52465292
5247- if (first_spread_index == expr-> arguments ()-> length () - 1 ) {
5293+ if (spread_position == Call:: kHasFinalSpread ) {
52485294 builder ()->ConstructWithSpread (constructor, args_regs,
52495295 feedback_slot_index);
52505296 } else {
5251- DCHECK_EQ (first_spread_index, expr-> arguments ()-> length () );
5297+ DCHECK_EQ (spread_position, Call:: kNoSpread );
52525298 // Call construct.
52535299 // TODO(turbofan): For now we do gather feedback on super constructor
52545300 // calls, utilizing the existing machinery to inline the actual call
0 commit comments