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:
parent
1d83f52ca9
commit
958d8e9f32
3
BUILD.gn
3
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 = ""
|
||||
|
@ -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);
|
||||
|
@ -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<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(®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);
|
||||
}
|
||||
|
@ -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)));
|
||||
|
Loading…
Reference in New Issue
Block a user