@@ -3111,6 +3111,8 @@ void BytecodeGenerator::BuildCreateArrayLiteral(
31113111 .StoreAccumulatorInRegister (index);
31123112 }
31133113 } else {
3114+ // TODO(v8:11582): Support allocating boilerplates here.
3115+
31143116 // In other cases, we prepare an empty array to be filled in below.
31153117 DCHECK (!elements->is_empty ());
31163118 int literal_index = feedback_index (feedback_spec ()->AddLiteralSlot ());
@@ -5027,17 +5029,30 @@ void BytecodeGenerator::VisitCall(Call* expr) {
50275029 return VisitCallSuper (expr);
50285030 }
50295031
5032+ // We compile the call differently depending on the presence of spreads and
5033+ // their positions.
5034+ //
5035+ // If there is only one spread and it is the final argument, there is a
5036+ // special CallWithSpread bytecode.
5037+ //
5038+ // If there is a non-final spread, we rewrite calls like
5039+ // callee(1, ...x, 2)
5040+ // to
5041+ // %reflect_apply(callee, receiver, [1, ...x, 2])
5042+ const Call::SpreadPosition spread_position = expr->spread_position ();
5043+
50305044 // Grow the args list as we visit receiver / arguments to avoid allocating all
50315045 // the registers up-front. Otherwise these registers are unavailable during
50325046 // receiver / argument visiting and we can end up with memory leaks due to
50335047 // registers keeping objects alive.
5034- Register callee = register_allocator ()->NewRegister ();
50355048 RegisterList args = register_allocator ()->NewGrowableRegisterList ();
50365049
5050+ // The callee is the first register in args for ease of calling %reflect_apply
5051+ // if we have a non-final spread. For all other cases it is popped from args
5052+ // before emitting the call below.
5053+ Register callee = register_allocator ()->GrowRegisterList (&args);
5054+
50375055 bool implicit_undefined_receiver = false ;
5038- // When a call contains a spread, a Call AST node is only created if there is
5039- // exactly one spread, and it is the last argument.
5040- bool is_spread_call = expr->only_last_arg_is_spread ();
50415056 bool optimize_as_one_shot = ShouldOptimizeAsOneShot ();
50425057
50435058 // TODO(petermarshall): We have a lot of call bytecodes that are very similar,
@@ -5057,7 +5072,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
50575072 }
50585073 case Call::GLOBAL_CALL: {
50595074 // Receiver is undefined for global calls.
5060- if (!is_spread_call && !optimize_as_one_shot) {
5075+ if (spread_position == Call:: kNoSpread && !optimize_as_one_shot) {
50615076 implicit_undefined_receiver = true ;
50625077 } else {
50635078 // TODO(leszeks): There's no special bytecode for tail calls or spread
@@ -5093,7 +5108,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
50935108 }
50945109 case Call::OTHER_CALL: {
50955110 // Receiver is undefined for other calls.
5096- if (!is_spread_call && !optimize_as_one_shot) {
5111+ if (spread_position == Call:: kNoSpread && !optimize_as_one_shot) {
50975112 implicit_undefined_receiver = true ;
50985113 } else {
50995114 // TODO(leszeks): There's no special bytecode for tail calls or spread
@@ -5142,25 +5157,51 @@ void BytecodeGenerator::VisitCall(Call* expr) {
51425157 BuildIncrementBlockCoverageCounterIfEnabled (right_range);
51435158 }
51445159
5145- // Evaluate all arguments to the function call and store in sequential args
5146- // registers.
5147- VisitArguments (expr->arguments (), &args);
5148- int receiver_arg_count = implicit_undefined_receiver ? 0 : 1 ;
5149- CHECK_EQ (receiver_arg_count + expr->arguments ()->length (),
5150- args.register_count ());
5160+ int receiver_arg_count = -1 ;
5161+ if (spread_position == Call::kHasNonFinalSpread ) {
5162+ // If we're building %reflect_apply, build the array literal and put it in
5163+ // the 3rd argument.
5164+ DCHECK (!implicit_undefined_receiver);
5165+ DCHECK_EQ (args.register_count (), 2 );
5166+ BuildCreateArrayLiteral (expr->arguments (), nullptr );
5167+ builder ()->StoreAccumulatorInRegister (
5168+ register_allocator ()->GrowRegisterList (&args));
5169+ } else {
5170+ // If we're not building %reflect_apply and don't need to build an array
5171+ // literal, pop the callee and evaluate all arguments to the function call
5172+ // and store in sequential args registers.
5173+ args = args.PopLeft ();
5174+ VisitArguments (expr->arguments (), &args);
5175+ receiver_arg_count = implicit_undefined_receiver ? 0 : 1 ;
5176+ CHECK_EQ (receiver_arg_count + expr->arguments ()->length (),
5177+ args.register_count ());
5178+ }
51515179
51525180 // Resolve callee for a potential direct eval call. This block will mutate the
51535181 // callee value.
51545182 if (expr->is_possibly_eval () && expr->arguments ()->length () > 0 ) {
51555183 RegisterAllocationScope inner_register_scope (this );
5184+ RegisterList runtime_call_args = register_allocator ()->NewRegisterList (6 );
51565185 // Set up arguments for ResolvePossiblyDirectEval by copying callee, source
51575186 // strings and function closure, and loading language and
51585187 // position.
5159- Register first_arg = args[receiver_arg_count];
5160- RegisterList runtime_call_args = register_allocator ()->NewRegisterList (6 );
5188+
5189+ // Move the first arg.
5190+ if (spread_position == Call::kHasNonFinalSpread ) {
5191+ int feedback_slot_index =
5192+ feedback_index (feedback_spec ()->AddKeyedLoadICSlot ());
5193+ Register args_array = args[2 ];
5194+ builder ()
5195+ ->LoadLiteral (Smi::FromInt (0 ))
5196+ .LoadKeyedProperty (args_array, feedback_slot_index)
5197+ .StoreAccumulatorInRegister (runtime_call_args[1 ]);
5198+ } else {
5199+ // FIXME(v8:5690): Support final spreads for eval.
5200+ DCHECK_GE (receiver_arg_count, 0 );
5201+ builder ()->MoveRegister (args[receiver_arg_count], runtime_call_args[1 ]);
5202+ }
51615203 builder ()
51625204 ->MoveRegister (callee, runtime_call_args[0 ])
5163- .MoveRegister (first_arg, runtime_call_args[1 ])
51645205 .MoveRegister (Register::function_closure (), runtime_call_args[2 ])
51655206 .LoadLiteral (Smi::FromEnum (language_mode ()))
51665207 .StoreAccumulatorInRegister (runtime_call_args[3 ])
@@ -5177,10 +5218,12 @@ void BytecodeGenerator::VisitCall(Call* expr) {
51775218
51785219 builder ()->SetExpressionPosition (expr);
51795220
5180- if (is_spread_call ) {
5221+ if (spread_position == Call:: kHasFinalSpread ) {
51815222 DCHECK (!implicit_undefined_receiver);
51825223 builder ()->CallWithSpread (callee, args,
51835224 feedback_index (feedback_spec ()->AddCallICSlot ()));
5225+ } else if (spread_position == Call::kHasNonFinalSpread ) {
5226+ builder ()->CallJSRuntime (Context::REFLECT_APPLY_INDEX, args);
51845227 } else if (optimize_as_one_shot) {
51855228 DCHECK (!implicit_undefined_receiver);
51865229 builder ()->CallNoFeedback (callee, args);
@@ -5203,10 +5246,20 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
52035246 SuperCallReference* super = expr->expression ()->AsSuperCallReference ();
52045247 const ZonePtrList<Expression>* args = expr->arguments ();
52055248
5206- int first_spread_index = 0 ;
5207- for (; first_spread_index < args->length (); first_spread_index++) {
5208- if (args->at (first_spread_index)->IsSpread ()) break ;
5209- }
5249+ // We compile the super call differently depending on the presence of spreads
5250+ // and their positions.
5251+ //
5252+ // If there is only one spread and it is the final argument, there is a
5253+ // special ConstructWithSpread bytecode.
5254+ //
5255+ // It there is a non-final spread, we rewrite something like
5256+ // super(1, ...x, 2)
5257+ // to
5258+ // %reflect_construct(constructor, [1, ...x, 2], new_target)
5259+ //
5260+ // That is, we implement (non-last-arg) spreads in super calls via our
5261+ // mechanism for spreads in array literals.
5262+ const Call::SpreadPosition spread_position = expr->spread_position ();
52105263
52115264 // Prepare the constructor to the super call.
52125265 Register this_function = VisitForRegisterValue (super->this_function_var ());
@@ -5215,14 +5268,7 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
52155268 ->LoadAccumulatorWithRegister (this_function)
52165269 .GetSuperConstructor (constructor);
52175270
5218- if (first_spread_index < expr->arguments ()->length () - 1 ) {
5219- // We rewrite something like
5220- // super(1, ...x, 2)
5221- // to
5222- // %reflect_construct(constructor, [1, ...x, 2], new_target)
5223- // That is, we implement (non-last-arg) spreads in super calls via our
5224- // mechanism for spreads in array literals.
5225-
5271+ if (spread_position == Call::kHasNonFinalSpread ) {
52265272 // First generate the array containing all arguments.
52275273 BuildCreateArrayLiteral (args, nullptr );
52285274
@@ -5249,11 +5295,11 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
52495295
52505296 int feedback_slot_index = feedback_index (feedback_spec ()->AddCallICSlot ());
52515297
5252- if (first_spread_index == expr-> arguments ()-> length () - 1 ) {
5298+ if (spread_position == Call:: kHasFinalSpread ) {
52535299 builder ()->ConstructWithSpread (constructor, args_regs,
52545300 feedback_slot_index);
52555301 } else {
5256- DCHECK_EQ (first_spread_index, expr-> arguments ()-> length () );
5302+ DCHECK_EQ (spread_position, Call:: kNoSpread );
52575303 // Call construct.
52585304 // TODO(turbofan): For now we do gather feedback on super constructor
52595305 // calls, utilizing the existing machinery to inline the actual call
0 commit comments