Reland "[ia32] Remove arguments adaptor frame"

This is a reland of 403390ec60

Original change's description:
> [ia32] Remove arguments adaptor frame
>
> Change-Id: Id66d2c57fc92c00b033bc53231313f477cceca75
> Bug: v8:10201
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2448463
> Reviewed-by: Georg Neis <neis@chromium.org>
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Commit-Queue: Victor Gomes <victorgomes@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#70652}

Bug: v8:10201
Change-Id: I2c50b22fbe565e8ad6a510c02bfbd79c145d284e
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2485225
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70663}
This commit is contained in:
Victor Gomes 2020-10-19 17:07:32 +02:00 committed by Commit Bot
parent 1d83f52ca9
commit 958d8e9f32
4 changed files with 177 additions and 35 deletions

View File

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

View File

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

View File

@ -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, &regular_invoke, Label::kNear);
// Skip if overapplication or if expected number of arguments.
sub(expected_parameter_count, actual_parameter_count);
j(less_equal, &regular_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(&copy);
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, &copy);
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, &regular_invoke);
Handle<Code> adaptor = BUILTIN_CODE(isolate(), ArgumentsAdaptorTrampoline);
@ -1128,6 +1184,7 @@ void MacroAssembler::InvokePrologue(Register expected_parameter_count,
} else {
Jump(adaptor, RelocInfo::CODE_TARGET);
}
#endif
bind(&regular_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);
}

View File

@ -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<int>(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<int>(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<size_t>(std::numeric_limits<int>::max()));
__ Ret(static_cast<int>(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<int>(parameter_count * kSystemPointerSize);
__ PopReturnAddressTo(scratch_reg);
__ lea(esp, Operand(esp, pop_reg, times_system_pointer_size,
static_cast<int>(pop_size)));