@@ -3106,6 +3106,8 @@ void BytecodeGenerator::BuildCreateArrayLiteral(
3106
3106
.StoreAccumulatorInRegister (index );
3107
3107
}
3108
3108
} else {
3109
+ // TODO(v8:11582): Support allocating boilerplates here.
3110
+
3109
3111
// In other cases, we prepare an empty array to be filled in below.
3110
3112
DCHECK (!elements->is_empty ());
3111
3113
int literal_index = feedback_index (feedback_spec ()->AddLiteralSlot ());
@@ -5022,17 +5024,30 @@ void BytecodeGenerator::VisitCall(Call* expr) {
5022
5024
return VisitCallSuper (expr);
5023
5025
}
5024
5026
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
+
5025
5039
// Grow the args list as we visit receiver / arguments to avoid allocating all
5026
5040
// the registers up-front. Otherwise these registers are unavailable during
5027
5041
// receiver / argument visiting and we can end up with memory leaks due to
5028
5042
// registers keeping objects alive.
5029
- Register callee = register_allocator ()->NewRegister ();
5030
5043
RegisterList args = register_allocator ()->NewGrowableRegisterList ();
5031
5044
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
+
5032
5050
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 ();
5036
5051
bool optimize_as_one_shot = ShouldOptimizeAsOneShot ();
5037
5052
5038
5053
// TODO(petermarshall): We have a lot of call bytecodes that are very similar,
@@ -5052,7 +5067,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
5052
5067
}
5053
5068
case Call::GLOBAL_CALL: {
5054
5069
// 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) {
5056
5071
implicit_undefined_receiver = true ;
5057
5072
} else {
5058
5073
// TODO(leszeks): There's no special bytecode for tail calls or spread
@@ -5088,7 +5103,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
5088
5103
}
5089
5104
case Call::OTHER_CALL: {
5090
5105
// 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) {
5092
5107
implicit_undefined_receiver = true ;
5093
5108
} else {
5094
5109
// TODO(leszeks): There's no special bytecode for tail calls or spread
@@ -5137,25 +5152,51 @@ void BytecodeGenerator::VisitCall(Call* expr) {
5137
5152
BuildIncrementBlockCoverageCounterIfEnabled (right_range);
5138
5153
}
5139
5154
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
+ }
5146
5174
5147
5175
// Resolve callee for a potential direct eval call. This block will mutate the
5148
5176
// callee value.
5149
5177
if (expr->is_possibly_eval () && expr->arguments ()->length () > 0 ) {
5150
5178
RegisterAllocationScope inner_register_scope (this );
5179
+ RegisterList runtime_call_args = register_allocator ()->NewRegisterList (6 );
5151
5180
// Set up arguments for ResolvePossiblyDirectEval by copying callee, source
5152
5181
// strings and function closure, and loading language and
5153
5182
// 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
+ }
5156
5198
builder ()
5157
5199
->MoveRegister (callee, runtime_call_args[0 ])
5158
- .MoveRegister (first_arg, runtime_call_args[1 ])
5159
5200
.MoveRegister (Register::function_closure (), runtime_call_args[2 ])
5160
5201
.LoadLiteral (Smi::FromEnum (language_mode ()))
5161
5202
.StoreAccumulatorInRegister (runtime_call_args[3 ])
@@ -5172,10 +5213,12 @@ void BytecodeGenerator::VisitCall(Call* expr) {
5172
5213
5173
5214
builder ()->SetExpressionPosition (expr);
5174
5215
5175
- if (is_spread_call ) {
5216
+ if (spread_position == Call:: kHasFinalSpread ) {
5176
5217
DCHECK (!implicit_undefined_receiver);
5177
5218
builder ()->CallWithSpread (callee, args,
5178
5219
feedback_index (feedback_spec ()->AddCallICSlot ()));
5220
+ } else if (spread_position == Call::kHasNonFinalSpread ) {
5221
+ builder ()->CallJSRuntime (Context::REFLECT_APPLY_INDEX, args);
5179
5222
} else if (optimize_as_one_shot) {
5180
5223
DCHECK (!implicit_undefined_receiver);
5181
5224
builder ()->CallNoFeedback (callee, args);
@@ -5198,10 +5241,20 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
5198
5241
SuperCallReference* super = expr->expression ()->AsSuperCallReference ();
5199
5242
const ZonePtrList<Expression>* args = expr->arguments ();
5200
5243
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 ();
5205
5258
5206
5259
// Prepare the constructor to the super call.
5207
5260
Register this_function = VisitForRegisterValue (super->this_function_var ());
@@ -5210,14 +5263,7 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
5210
5263
->LoadAccumulatorWithRegister (this_function)
5211
5264
.GetSuperConstructor (constructor);
5212
5265
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 ) {
5221
5267
// First generate the array containing all arguments.
5222
5268
BuildCreateArrayLiteral (args, nullptr );
5223
5269
@@ -5244,11 +5290,11 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
5244
5290
5245
5291
int feedback_slot_index = feedback_index (feedback_spec ()->AddCallICSlot ());
5246
5292
5247
- if (first_spread_index == expr-> arguments ()-> length () - 1 ) {
5293
+ if (spread_position == Call:: kHasFinalSpread ) {
5248
5294
builder ()->ConstructWithSpread (constructor, args_regs,
5249
5295
feedback_slot_index);
5250
5296
} else {
5251
- DCHECK_EQ (first_spread_index, expr-> arguments ()-> length () );
5297
+ DCHECK_EQ (spread_position, Call:: kNoSpread );
5252
5298
// Call construct.
5253
5299
// TODO(turbofan): For now we do gather feedback on super constructor
5254
5300
// calls, utilizing the existing machinery to inline the actual call
0 commit comments