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