diff --git a/BUILD.gn b/BUILD.gn index c59bbff58a..59b0c99529 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -110,7 +110,8 @@ declare_args() { v8_enable_31bit_smis_on_64bit_arch = false # Disable arguments adaptor frame (sets -dV8_NO_ARGUMENTS_ADAPTOR). - v8_disable_arguments_adaptor = v8_current_cpu == "x64" + v8_disable_arguments_adaptor = + v8_current_cpu == "x86" || v8_current_cpu == "x64" # Sets -dOBJECT_PRINT. v8_enable_object_print = "" diff --git a/src/builtins/ia32/builtins-ia32.cc b/src/builtins/ia32/builtins-ia32.cc index 9a15af1bde..3f428c38bc 100644 --- a/src/builtins/ia32/builtins-ia32.cc +++ b/src/builtins/ia32/builtins-ia32.cc @@ -136,6 +136,11 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { __ push(eax); __ SmiUntag(eax); + // TODO(victorgomes): When the arguments adaptor is completely removed, we + // should get the formal parameter count and copy the arguments in its + // correct position (including any undefined), instead of delaying this to + // InvokeFunction. + // Set up pointer to first argument (skip receiver). __ lea(esi, Operand(ebp, StandardFrameConstants::kCallerSPOffset + kSystemPointerSize)); @@ -275,6 +280,11 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ bind(&enough_stack_space); + // TODO(victorgomes): When the arguments adaptor is completely removed, we + // should get the formal parameter count and copy the arguments in its + // correct position (including any undefined), instead of delaying this to + // InvokeFunction. + // Copy arguments to the expression stack. __ PushArray(edi, eax, ecx); @@ -739,22 +749,38 @@ static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm, static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1, Register scratch2) { - Register args_count = scratch1; - Register return_pc = scratch2; - - // Get the arguments + receiver count. - __ mov(args_count, + Register params_size = scratch1; + // Get the size of the formal parameters + receiver (in bytes). + __ mov(params_size, Operand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp)); - __ mov(args_count, - FieldOperand(args_count, BytecodeArray::kParameterSizeOffset)); + __ mov(params_size, + FieldOperand(params_size, BytecodeArray::kParameterSizeOffset)); + +#ifdef V8_NO_ARGUMENTS_ADAPTOR + Register actual_params_size = scratch2; + // Compute the size of the actual parameters + receiver (in bytes). + __ mov(actual_params_size, Operand(ebp, StandardFrameConstants::kArgCOffset)); + __ lea(actual_params_size, + Operand(actual_params_size, times_system_pointer_size, + kSystemPointerSize)); + + // If actual is bigger than formal, then we should use it to free up the stack + // arguments. + Label corrected_args_count; + __ cmp(params_size, actual_params_size); + __ j(greater_equal, &corrected_args_count, Label::kNear); + __ mov(params_size, actual_params_size); + __ bind(&corrected_args_count); +#endif // Leave the frame (also dropping the register file). __ leave(); // Drop receiver + arguments. - __ pop(return_pc); - __ add(esp, args_count); - __ push(return_pc); + Register return_pc = scratch2; + __ PopReturnAddressTo(return_pc); + __ add(esp, params_size); + __ PushReturnAddressFrom(return_pc); } // Tail-call |function_id| if |smi_entry| == |marker| @@ -779,8 +805,8 @@ static void TailCallOptimizedCodeSlot(MacroAssembler* masm, DCHECK(!AreAliased(edx, edi, optimized_code_entry)); Register closure = edi; - - __ push(edx); + __ movd(xmm0, eax); + __ movd(xmm1, edx); // Check if the optimized code is marked for deopt. If it is, bailout to a // given label. @@ -797,13 +823,15 @@ static void TailCallOptimizedCodeSlot(MacroAssembler* masm, eax); static_assert(kJavaScriptCallCodeStartRegister == ecx, "ABI mismatch"); __ LoadCodeObjectEntry(ecx, optimized_code_entry); - __ pop(edx); + __ movd(edx, xmm1); + __ movd(eax, xmm0); __ jmp(ecx); // Optimized code slot contains deoptimized code, evict it and re-enter // the closure's code. __ bind(&found_deoptimized_code); - __ pop(edx); + __ movd(edx, xmm1); + __ movd(eax, xmm0); GenerateTailCallToReturnedCode(masm, Runtime::kEvictOptimizedCodeSlot); } @@ -2051,6 +2079,12 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, __ movd(xmm1, edx); // Preserve new.target (in case of [[Construct]]). +#ifdef V8_NO_ARGUMENTS_ADAPTOR + // TODO(victorgomes): Remove this copy when all the arguments adaptor frame + // code is erased. + __ mov(scratch, ebp); + __ mov(edx, Operand(ebp, StandardFrameConstants::kArgCOffset)); +#else // Check if we have an arguments adaptor frame below the function frame. Label arguments_adaptor, arguments_done; __ mov(scratch, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); @@ -2073,6 +2107,7 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, __ SmiUntag(edx); } __ bind(&arguments_done); +#endif Label stack_done, stack_overflow; __ sub(edx, ecx); diff --git a/src/codegen/ia32/macro-assembler-ia32.cc b/src/codegen/ia32/macro-assembler-ia32.cc index 3a7ff9771a..d9d8bb0f2f 100644 --- a/src/codegen/ia32/macro-assembler-ia32.cc +++ b/src/codegen/ia32/macro-assembler-ia32.cc @@ -1113,12 +1113,68 @@ void TurboAssembler::PrepareForTailCall( void MacroAssembler::InvokePrologue(Register expected_parameter_count, Register actual_parameter_count, Label* done, InvokeFlag flag) { - DCHECK_EQ(actual_parameter_count, eax); - if (expected_parameter_count != actual_parameter_count) { + DCHECK_EQ(actual_parameter_count, eax); DCHECK_EQ(expected_parameter_count, ecx); - Label regular_invoke; +#ifdef V8_NO_ARGUMENTS_ADAPTOR + // Skip if adaptor sentinel. + cmp(expected_parameter_count, Immediate(kDontAdaptArgumentsSentinel)); + j(equal, ®ular_invoke, Label::kNear); + + // Skip if overapplication or if expected number of arguments. + sub(expected_parameter_count, actual_parameter_count); + j(less_equal, ®ular_invoke, Label::kNear); + + // We need to preserve edx, edi, esi and ebx. + movd(xmm0, edx); + movd(xmm1, edi); + movd(xmm2, esi); + movd(xmm3, ebx); + + Register scratch = esi; + + // Underapplication. Move the arguments already in the stack, including the + // receiver and the return address. + { + Label copy, check; + Register src = edx, dest = esp, num = edi, current = ebx; + mov(src, esp); + lea(scratch, + Operand(expected_parameter_count, times_system_pointer_size, 0)); + AllocateStackSpace(scratch); + // Extra words are the receiver and the return address (if a jump). + int extra_words = flag == CALL_FUNCTION ? 1 : 2; + lea(num, Operand(eax, extra_words)); // Number of words to copy. + Set(current, 0); + // Fall-through to the loop body because there are non-zero words to copy. + bind(©); + mov(scratch, Operand(src, current, times_system_pointer_size, 0)); + mov(Operand(dest, current, times_system_pointer_size, 0), scratch); + inc(current); + bind(&check); + cmp(current, num); + j(less, ©); + lea(edx, Operand(esp, num, times_system_pointer_size, 0)); + } + + // Fill remaining expected arguments with undefined values. + movd(ebx, xmm3); // Restore root. + LoadRoot(scratch, RootIndex::kUndefinedValue); + { + Label loop; + bind(&loop); + dec(expected_parameter_count); + mov(Operand(edx, expected_parameter_count, times_system_pointer_size, 0), + scratch); + j(greater, &loop, Label::kNear); + } + + // Restore remaining registers. + movd(esi, xmm2); + movd(edi, xmm1); + movd(edx, xmm0); +#else cmp(expected_parameter_count, actual_parameter_count); j(equal, ®ular_invoke); Handle adaptor = BUILTIN_CODE(isolate(), ArgumentsAdaptorTrampoline); @@ -1128,6 +1184,7 @@ void MacroAssembler::InvokePrologue(Register expected_parameter_count, } else { Jump(adaptor, RelocInfo::CODE_TARGET); } +#endif bind(®ular_invoke); } } @@ -1182,7 +1239,7 @@ void MacroAssembler::InvokeFunctionCode(Register function, Register new_target, push(eax); cmpb(ExternalReferenceAsOperand(debug_hook_active, eax), Immediate(0)); pop(eax); - j(not_equal, &debug_hook, Label::kNear); + j(not_equal, &debug_hook); } bind(&continue_after_hook); @@ -1210,7 +1267,7 @@ void MacroAssembler::InvokeFunctionCode(Register function, Register new_target, bind(&debug_hook); CallDebugOnFunctionCall(function, new_target, expected_parameter_count, actual_parameter_count); - jmp(&continue_after_hook, Label::kNear); + jmp(&continue_after_hook); bind(&done); } diff --git a/src/compiler/backend/ia32/code-generator-ia32.cc b/src/compiler/backend/ia32/code-generator-ia32.cc index 9e902c070f..ffa635e21e 100644 --- a/src/compiler/backend/ia32/code-generator-ia32.cc +++ b/src/compiler/backend/ia32/code-generator-ia32.cc @@ -4784,7 +4784,7 @@ void CodeGenerator::AssembleConstructFrame() { } } -void CodeGenerator::AssembleReturn(InstructionOperand* pop) { +void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { auto call_descriptor = linkage()->GetIncomingDescriptor(); const RegList saves = call_descriptor->CalleeSavedRegisters(); @@ -4800,37 +4800,86 @@ void CodeGenerator::AssembleReturn(InstructionOperand* pop) { } } - // Might need ecx for scratch if pop_size is too big or if there is a variable - // pop count. + // We might need ecx and edx for scratch. + DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & edx.bit()); DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & ecx.bit()); - size_t pop_size = call_descriptor->StackParameterCount() * kSystemPointerSize; IA32OperandConverter g(this, nullptr); + int parameter_count = + static_cast(call_descriptor->StackParameterCount()); + + // {aditional_pop_count} is only greater than zero if {parameter_count = 0}. + // Check RawMachineAssembler::PopAndReturn. + if (parameter_count != 0) { + if (additional_pop_count->IsImmediate()) { + DCHECK_EQ(g.ToConstant(additional_pop_count).ToInt32(), 0); + } else if (__ emit_debug_code()) { + __ cmp(g.ToRegister(additional_pop_count), Immediate(0)); + __ Assert(equal, AbortReason::kUnexpectedAdditionalPopValue); + } + } + + Register argc_reg = ecx; +#ifdef V8_NO_ARGUMENTS_ADAPTOR + // Functions with JS linkage have at least one parameter (the receiver). + // If {parameter_count} == 0, it means it is a builtin with + // kDontAdaptArgumentsSentinel, which takes care of JS arguments popping + // itself. + const bool drop_jsargs = frame_access_state()->has_frame() && + call_descriptor->IsJSFunctionCall() && + parameter_count != 0; +#else + const bool drop_jsargs = false; +#endif if (call_descriptor->IsCFunctionCall()) { AssembleDeconstructFrame(); } else if (frame_access_state()->has_frame()) { // Canonicalize JSFunction return sites for now if they always have the same // number of return args. - if (pop->IsImmediate() && g.ToConstant(pop).ToInt32() == 0) { + if (additional_pop_count->IsImmediate() && + g.ToConstant(additional_pop_count).ToInt32() == 0) { if (return_label_.is_bound()) { __ jmp(&return_label_); return; } else { __ bind(&return_label_); - AssembleDeconstructFrame(); } - } else { - AssembleDeconstructFrame(); } + if (drop_jsargs) { + // Get the actual argument count. + __ mov(argc_reg, Operand(ebp, StandardFrameConstants::kArgCOffset)); + } + AssembleDeconstructFrame(); } - DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & edx.bit()); - DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & ecx.bit()); - if (pop->IsImmediate()) { - DCHECK_EQ(Constant::kInt32, g.ToConstant(pop).type()); - pop_size += g.ToConstant(pop).ToInt32() * kSystemPointerSize; - __ Ret(static_cast(pop_size), ecx); + + if (drop_jsargs) { + // We must pop all arguments from the stack (including the receiver). This + // number of arguments is given by max(1 + argc_reg, parameter_count). + int parameter_count_without_receiver = + parameter_count - 1; // Exclude the receiver to simplify the + // computation. We'll account for it at the end. + Label mismatch_return; + Register scratch_reg = edx; + DCHECK_NE(argc_reg, scratch_reg); + __ cmp(argc_reg, Immediate(parameter_count_without_receiver)); + __ j(greater, &mismatch_return, Label::kNear); + __ Ret(parameter_count * kSystemPointerSize, scratch_reg); + __ bind(&mismatch_return); + __ PopReturnAddressTo(scratch_reg); + __ lea(esp, Operand(esp, argc_reg, times_system_pointer_size, + kSystemPointerSize)); // Also pop the receiver. + // We use a return instead of a jump for better return address prediction. + __ PushReturnAddressFrom(scratch_reg); + __ Ret(); + } else if (additional_pop_count->IsImmediate()) { + Register scratch_reg = ecx; + int additional_count = g.ToConstant(additional_pop_count).ToInt32(); + size_t pop_size = (parameter_count + additional_count) * kSystemPointerSize; + CHECK_LE(pop_size, static_cast(std::numeric_limits::max())); + __ Ret(static_cast(pop_size), scratch_reg); } else { - Register pop_reg = g.ToRegister(pop); + Register pop_reg = g.ToRegister(additional_pop_count); Register scratch_reg = pop_reg == ecx ? edx : ecx; + int pop_size = static_cast(parameter_count * kSystemPointerSize); __ PopReturnAddressTo(scratch_reg); __ lea(esp, Operand(esp, pop_reg, times_system_pointer_size, static_cast(pop_size)));