@@ -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