Commit 52bebb7b authored by Victor Gomes's avatar Victor Gomes Committed by Commit Bot

[compiler][x64] Remove arguments adaptor frame

Removes the arguments adaptor frame during optimization (for x64) and deoptimization.

Change-Id: Ica78ebbb9216555dd3f1adf05d6b293e8add0050
Bug: v8:10201
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2410195
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70194}
parent f542fdef
......@@ -2382,6 +2382,12 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
__ bind(&new_target_constructor);
}
#ifdef V8_NO_ARGUMENTS_ADAPTOR
// TODO(victorgomes): Remove this copy when all the arguments adaptor frame
// code is erased.
__ movq(rbx, rbp);
__ movq(r8, Operand(rbp, StandardFrameConstants::kArgCOffset));
#else
// Check if we have an arguments adaptor frame below the function frame.
Label arguments_adaptor, arguments_done;
__ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
......@@ -2403,6 +2409,7 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset));
}
__ bind(&arguments_done);
#endif
Label stack_done, stack_overflow;
__ subl(r8, rcx);
......
......@@ -4559,7 +4559,7 @@ void CodeGenerator::AssembleConstructFrame() {
}
}
void CodeGenerator::AssembleReturn(InstructionOperand* pop) {
void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
auto call_descriptor = linkage()->GetIncomingDescriptor();
// Restore registers.
......@@ -4592,38 +4592,89 @@ void CodeGenerator::AssembleReturn(InstructionOperand* pop) {
unwinding_info_writer_.MarkBlockWillExit();
// Might need rcx for scratch if pop_size is too big or if there is a variable
// pop count.
// We might need rcx and rdx for scratch.
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & rcx.bit());
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & rdx.bit());
size_t pop_size = call_descriptor->StackParameterCount() * kSystemPointerSize;
int parameter_count =
static_cast<int>(call_descriptor->StackParameterCount());
X64OperandConverter g(this, nullptr);
Register pop_reg = additional_pop_count->IsImmediate()
? rcx
: g.ToRegister(additional_pop_count);
Register scratch_reg = pop_reg == rcx ? rdx : rcx;
Register argc_reg =
additional_pop_count->IsImmediate() ? pop_reg : scratch_reg;
#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()) {
if (pop->IsImmediate() && g.ToConstant(pop).ToInt32() == 0) {
if (additional_pop_count->IsImmediate() &&
g.ToConstant(additional_pop_count).ToInt32() == 0) {
// Canonicalize JSFunction return sites for now.
if (return_label_.is_bound()) {
__ jmp(&return_label_);
return;
} else {
__ bind(&return_label_);
AssembleDeconstructFrame();
}
} else {
AssembleDeconstructFrame();
}
if (drop_jsargs) {
// Get the actual argument count.
__ movq(argc_reg, Operand(rbp, StandardFrameConstants::kArgCOffset));
}
AssembleDeconstructFrame();
}
if (pop->IsImmediate()) {
pop_size += g.ToConstant(pop).ToInt32() * kSystemPointerSize;
CHECK_LT(pop_size, static_cast<size_t>(std::numeric_limits<int>::max()));
__ Ret(static_cast<int>(pop_size), rcx);
if (drop_jsargs) {
// In addition to the slots given by {additional_pop_count}, we must pop all
// arguments from the stack (including the receiver). This number of
// arguments is given by max(1 + argc_reg, parameter_count).
Label argc_reg_has_final_count;
// Exclude the receiver to simplify the computation. We'll account for it at
// the end.
int parameter_count_withouth_receiver = parameter_count - 1;
if (parameter_count_withouth_receiver != 0) {
__ cmpq(argc_reg, Immediate(parameter_count_withouth_receiver));
__ j(greater_equal, &argc_reg_has_final_count, Label::kNear);
__ movq(argc_reg, Immediate(parameter_count_withouth_receiver));
__ bind(&argc_reg_has_final_count);
}
// Add additional pop count.
if (additional_pop_count->IsImmediate()) {
DCHECK_EQ(pop_reg, argc_reg);
int additional_count = g.ToConstant(additional_pop_count).ToInt32();
if (additional_count != 0) {
__ addq(pop_reg, Immediate(additional_count));
}
} else {
__ addq(pop_reg, argc_reg);
}
__ PopReturnAddressTo(scratch_reg);
__ leaq(rsp, Operand(rsp, pop_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()) {
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 scratch_reg = pop_reg == rcx ? rdx : rcx;
int pop_size = static_cast<int>(parameter_count * kSystemPointerSize);
__ PopReturnAddressTo(scratch_reg);
__ leaq(rsp, Operand(rsp, pop_reg, times_8, static_cast<int>(pop_size)));
__ leaq(rsp, Operand(rsp, pop_reg, times_system_pointer_size,
static_cast<int>(pop_size)));
__ PushReturnAddressFrom(scratch_reg);
__ Ret();
}
......
......@@ -3850,6 +3850,14 @@ Node* EffectControlLinearizer::LowerToBoolean(Node* node) {
}
Node* EffectControlLinearizer::LowerArgumentsLength(Node* node) {
#ifdef V8_NO_ARGUMENTS_ADAPTOR
return ChangeIntPtrToSmi(
__ Load(MachineType::Pointer(), __ LoadFramePointer(),
__ IntPtrConstant(StandardFrameConstants::kArgCOffset)));
#else
auto done = __ MakeLabel(MachineRepresentation::kTaggedSigned);
Node* frame = __ LoadFramePointer();
Node* arguments_frame = NodeProperties::GetValueInput(node, 0);
int formal_parameter_count = FormalParameterCountOf(node->op());
DCHECK_LE(0, formal_parameter_count);
......@@ -3858,9 +3866,6 @@ Node* EffectControlLinearizer::LowerArgumentsLength(Node* node) {
// We have to distinguish the case when there is an arguments adaptor frame
// (i.e., arguments_frame != LoadFramePointer()).
auto if_adaptor_frame = __ MakeLabel();
auto done = __ MakeLabel(MachineRepresentation::kTaggedSigned);
Node* frame = __ LoadFramePointer();
__ GotoIf(__ TaggedEqual(arguments_frame, frame), &done,
__ SmiConstant(formal_parameter_count));
__ Goto(&if_adaptor_frame);
......@@ -3870,24 +3875,30 @@ Node* EffectControlLinearizer::LowerArgumentsLength(Node* node) {
MachineType::Pointer(), arguments_frame,
__ IntPtrConstant(ArgumentsAdaptorFrameConstants::kLengthOffset)));
__ Goto(&done, arguments_length);
__ Bind(&done);
return done.PhiAt(0);
#endif
}
Node* EffectControlLinearizer::LowerRestLength(Node* node) {
Node* arguments_frame = NodeProperties::GetValueInput(node, 0);
int formal_parameter_count = FormalParameterCountOf(node->op());
DCHECK_LE(0, formal_parameter_count);
auto done = __ MakeLabel(MachineRepresentation::kTaggedSigned);
Node* frame = __ LoadFramePointer();
#ifdef V8_NO_ARGUMENTS_ADAPTOR
Node* arguments_length = ChangeIntPtrToSmi(
__ Load(MachineType::Pointer(), frame,
__ IntPtrConstant(StandardFrameConstants::kArgCOffset)));
#else
Node* arguments_frame = NodeProperties::GetValueInput(node, 0);
// The RestLength node is computing the number of rest parameters,
// which is max(0, actual_parameter_count - formal_parameter_count).
// We have to distinguish the case, when there is an arguments adaptor frame
// (i.e., arguments_frame != LoadFramePointer()).
auto if_adaptor_frame = __ MakeLabel();
auto done = __ MakeLabel(MachineRepresentation::kTaggedSigned);
Node* frame = __ LoadFramePointer();
__ GotoIf(__ TaggedEqual(arguments_frame, frame), &done, __ SmiConstant(0));
__ Goto(&if_adaptor_frame);
......@@ -3895,6 +3906,7 @@ Node* EffectControlLinearizer::LowerRestLength(Node* node) {
Node* arguments_length = __ BitcastWordToTaggedSigned(__ Load(
MachineType::Pointer(), arguments_frame,
__ IntPtrConstant(ArgumentsAdaptorFrameConstants::kLengthOffset)));
#endif
Node* rest_length =
__ SmiSub(arguments_length, __ SmiConstant(formal_parameter_count));
......
......@@ -1589,11 +1589,13 @@ void ReduceBuiltin(JSGraph* jsgraph, Node* node, int builtin_index, int arity,
NodeProperties::ChangeOp(node, jsgraph->common()->Call(call_descriptor));
}
#ifndef V8_NO_ARGUMENTS_ADAPTOR
bool NeedsArgumentAdaptorFrame(SharedFunctionInfoRef shared, int arity) {
static const int sentinel = kDontAdaptArgumentsSentinel;
const int num_decl_parms = shared.internal_formal_parameter_count();
return (num_decl_parms != arity && num_decl_parms != sentinel);
}
#endif
} // namespace
......@@ -1779,6 +1781,26 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
Node* new_target = jsgraph()->UndefinedConstant();
#ifdef V8_NO_ARGUMENTS_ADAPTOR
int formal_count = shared->internal_formal_parameter_count();
if (formal_count != kDontAdaptArgumentsSentinel && formal_count > arity) {
node->RemoveInput(n.FeedbackVectorIndex());
// Underapplication. Massage the arguments to match the expected number of
// arguments.
for (int i = arity; i < formal_count; i++) {
node->InsertInput(graph()->zone(), arity + 2,
jsgraph()->UndefinedConstant());
}
// Patch {node} to a direct call.
node->InsertInput(graph()->zone(), formal_count + 2, new_target);
node->InsertInput(graph()->zone(), formal_count + 3,
jsgraph()->Constant(arity));
NodeProperties::ChangeOp(node,
common()->Call(Linkage::GetJSCallDescriptor(
graph()->zone(), false, 1 + formal_count,
flags | CallDescriptor::kCanUseRoots)));
#else
if (NeedsArgumentAdaptorFrame(*shared, arity)) {
node->RemoveInput(n.FeedbackVectorIndex());
......@@ -1826,6 +1848,7 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
common()->Call(Linkage::GetStubCallDescriptor(
graph()->zone(), callable.descriptor(), 1 + arity, flags)));
}
#endif
} else if (shared->HasBuiltinId() &&
Builtins::IsCpp(shared->builtin_id())) {
// Patch {node} to a direct CEntry call.
......
......@@ -851,8 +851,8 @@ void Deoptimizer::DoComputeOutputFrames() {
Memory<intptr_t>(fp_address + CommonFrameConstants::kCallerPCOffset);
input_frame_context_ = Memory<intptr_t>(
fp_address + CommonFrameConstants::kContextOrFrameTypeOffset);
actual_argument_count_ =
Memory<intptr_t>(fp_address + StandardFrameConstants::kArgCOffset);
actual_argument_count_ = static_cast<int>(
Memory<intptr_t>(fp_address + StandardFrameConstants::kArgCOffset));
if (FLAG_enable_embedded_constant_pool) {
caller_constant_pool_ = Memory<intptr_t>(
......@@ -877,11 +877,12 @@ void Deoptimizer::DoComputeOutputFrames() {
verbose_tracing_enabled() ? trace_scope()->file() : nullptr;
TranslationIterator state_iterator(translations, translation_index);
translated_state_.Init(
isolate_, input_->GetFramePointerAddress(), &state_iterator,
isolate_, input_->GetFramePointerAddress(), stack_fp_, &state_iterator,
input_data.LiteralArray(), input_->GetRegisterValues(), trace_file,
function_.IsHeapObject()
? function_.shared().internal_formal_parameter_count()
: 0);
: 0,
actual_argument_count_);
// Do the input frame to output frame(s) translation.
size_t count = translated_state_.frames().size();
......@@ -1022,6 +1023,13 @@ void Deoptimizer::DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
if (ShouldPadArguments(parameters_count)) {
frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
}
// Note: parameters_count includes the receiver.
if (verbose_tracing_enabled() && is_bottommost &&
actual_argument_count_ > parameters_count - 1) {
PrintF(trace_scope_->file(),
" -- %d extra argument(s) already in the stack --\n",
actual_argument_count_ - parameters_count + 1);
}
frame_writer.PushStackJSArguments(value_iterator, parameters_count);
DCHECK_EQ(output_frame->GetLastArgumentSlotOffset(),
......@@ -1093,7 +1101,17 @@ void Deoptimizer::DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
frame_writer.PushTranslatedValue(function_iterator, "function");
// Actual argument count.
frame_writer.PushRawValue(actual_argument_count_, "actual argument count\n");
int argc;
if (is_bottommost) {
argc = actual_argument_count_;
} else {
TranslatedFrame::Kind previous_frame_kind =
(translated_state_.frames()[frame_index - 1]).kind();
argc = previous_frame_kind == TranslatedFrame::kArgumentsAdaptor
? output_[frame_index - 1]->parameter_count()
: parameters_count - 1;
}
frame_writer.PushRawValue(argc, "actual argument count\n");
// Set the bytecode array pointer.
Object bytecode_array = shared.HasBreakInfo()
......@@ -1238,6 +1256,64 @@ void Deoptimizer::DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
void Deoptimizer::DoComputeArgumentsAdaptorFrame(
TranslatedFrame* translated_frame, int frame_index) {
// Arguments adaptor can not be top most, nor the bottom most frames.
CHECK(frame_index < output_count_ - 1);
CHECK_GT(frame_index, 0);
CHECK_NULL(output_[frame_index]);
#ifdef V8_NO_ARGUMENTS_ADAPTOR
// During execution, V8 does not understand arguments adaptor frames anymore,
// so during deoptimization we only push the extra arguments (arguments with
// index greater than the formal parameter count). Therefore we call this
// TranslatedFrame the fake adaptor frame. For more info, see the design
// document shorturl.at/fKT49.
TranslatedFrame::iterator value_iterator = translated_frame->begin();
const int argument_count_without_receiver = translated_frame->height() - 1;
const int formal_parameter_count =
translated_frame->raw_shared_info().internal_formal_parameter_count();
const int extra_argument_count =
argument_count_without_receiver - formal_parameter_count;
const int output_frame_size =
std::max(0, extra_argument_count * kSystemPointerSize);
if (verbose_tracing_enabled()) {
PrintF(trace_scope_->file(),
" translating arguments adaptor => variable_size=%d\n",
output_frame_size);
}
// Allocate and store the output frame description.
FrameDescription* output_frame = new (output_frame_size)
FrameDescription(output_frame_size, argument_count_without_receiver);
// The top address of the frame is computed from the previous frame's top and
// this frame's size.
const intptr_t top_address =
output_[frame_index - 1]->GetTop() - output_frame_size;
output_frame->SetTop(top_address);
// This is not a real frame, we take PC and FP values from the parent frame.
output_frame->SetPc(output_[frame_index - 1]->GetPc());
output_frame->SetFp(output_[frame_index - 1]->GetFp());
output_[frame_index] = output_frame;
if (extra_argument_count > 0) {
FrameWriter frame_writer(this, output_frame, verbose_trace_scope());
ReadOnlyRoots roots(isolate());
if (ShouldPadArguments(extra_argument_count)) {
frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
}
// The receiver and arguments with index below the formal parameter
// count are in the fake adaptor frame, because they are used to create the
// arguments object. We should however not push them, since the interpreter
// frame with do that.
value_iterator++; // Skip function.
value_iterator++; // Skip receiver.
for (int i = 0; i < formal_parameter_count; i++) value_iterator++;
frame_writer.PushStackJSArguments(value_iterator, extra_argument_count);
}
#else
TranslatedFrame::iterator value_iterator = translated_frame->begin();
const bool is_bottommost = (0 == frame_index);
......@@ -1334,6 +1410,7 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(
static_cast<intptr_t>(adaptor_trampoline.constant_pool());
output_frame->SetConstantPool(constant_pool_value);
}
#endif
}
void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
......@@ -3074,9 +3151,13 @@ void TranslatedState::CreateArgumentsElementsTranslatedValues(
FILE* trace_file) {
TranslatedFrame& frame = frames_[frame_index];
#ifdef V8_NO_ARGUMENTS_ADAPTOR
int arguments_length = actual_argument_count_;
#else
int arguments_length;
Address arguments_frame =
ComputeArgumentsPosition(input_frame_pointer, &arguments_length);
#endif
int length = type == CreateArgumentsType::kRestParameter
? std::max(0, arguments_length - formal_parameter_count_)
......@@ -3119,10 +3200,16 @@ void TranslatedState::CreateArgumentsElementsTranslatedValues(
int offset = i + start_index + 1;
#else
int offset = argc - i - 1;
#endif
#ifdef V8_NO_ARGUMENTS_ADAPTOR
Address arguments_frame = offset > formal_parameter_count_
? stack_frame_pointer_
: input_frame_pointer;
#endif
Address argument_slot = arguments_frame +
CommonFrameConstants::kFixedFrameSizeAboveFp +
offset * kSystemPointerSize;
frame.Add(TranslatedValue::NewTagged(this, *FullObjectSlot(argument_slot)));
}
}
......@@ -3179,8 +3266,12 @@ int TranslatedState::CreateNextTranslatedValue(
}
case Translation::ARGUMENTS_LENGTH: {
#ifdef V8_NO_ARGUMENTS_ADAPTOR
int arguments_length = actual_argument_count_;
#else
int arguments_length;
ComputeArgumentsPosition(fp, &arguments_length);
#endif
if (trace_file != nullptr) {
PrintF(trace_file, "arguments length field (length = %d)",
arguments_length);
......@@ -3465,18 +3556,28 @@ TranslatedState::TranslatedState(const JavaScriptFrame* frame) {
DCHECK(!data.is_null() && deopt_index != Safepoint::kNoDeoptimizationIndex);
TranslationIterator it(data.TranslationByteArray(),
data.TranslationIndex(deopt_index).value());
Init(frame->isolate(), frame->fp(), &it, data.LiteralArray(),
#ifdef V8_NO_ARGUMENT_ADAPTOR
int actual_argc = frame->GetActualArgumentCount();
#else
int actual_argc = 0;
#endif
Init(frame->isolate(), frame->fp(), kNullAddress, &it, data.LiteralArray(),
nullptr /* registers */, nullptr /* trace file */,
frame->function().shared().internal_formal_parameter_count());
frame->function().shared().internal_formal_parameter_count(),
actual_argc);
}
void TranslatedState::Init(Isolate* isolate, Address input_frame_pointer,
Address stack_frame_pointer,
TranslationIterator* iterator,
FixedArray literal_array, RegisterValues* registers,
FILE* trace_file, int formal_parameter_count) {
FILE* trace_file, int formal_parameter_count,
int actual_argument_count) {
DCHECK(frames_.empty());
stack_frame_pointer_ = stack_frame_pointer;
formal_parameter_count_ = formal_parameter_count;
actual_argument_count_ = actual_argument_count;
isolate_ = isolate;
// Read out the 'header' translation.
......
......@@ -342,8 +342,9 @@ class TranslatedState {
Isolate* isolate() { return isolate_; }
void Init(Isolate* isolate, Address input_frame_pointer,
TranslationIterator* iterator, FixedArray literal_array,
RegisterValues* registers, FILE* trace_file, int parameter_count);
Address stack_frame_pointer, TranslationIterator* iterator,
FixedArray literal_array, RegisterValues* registers,
FILE* trace_file, int parameter_count, int actual_argument_count);
void VerifyMaterializedObjects();
bool DoUpdateFeedback();
......@@ -411,6 +412,7 @@ class TranslatedState {
Isolate* isolate_ = nullptr;
Address stack_frame_pointer_ = kNullAddress;
int formal_parameter_count_;
int actual_argument_count_;
struct ObjectPosition {
int frame_index_;
......@@ -631,7 +633,9 @@ class Deoptimizer : public Malloced {
intptr_t caller_pc_;
intptr_t caller_constant_pool_;
intptr_t input_frame_context_;
intptr_t actual_argument_count_;
// The argument count of the bottom most frame.
int actual_argument_count_;
// Key for lookup of previously materialized objects
intptr_t stack_fp_;
......
......@@ -202,9 +202,14 @@ inline JavaScriptFrame::JavaScriptFrame(StackFrameIteratorBase* iterator)
: StandardFrame(iterator) {}
Address JavaScriptFrame::GetParameterSlot(int index) const {
DCHECK(-1 <= index &&
(index < ComputeParametersCount() ||
ComputeParametersCount() == kDontAdaptArgumentsSentinel));
DCHECK_LE(-1, index);
#ifdef V8_NO_ARGUMENTS_ADAPTOR
DCHECK_LT(index,
std::max(GetActualArgumentsCount(), ComputeParametersCount()));
#else
DCHECK(index < ComputeParametersCount() ||
ComputeParametersCount() == kDontAdaptArgumentsSentinel);
#endif
#ifdef V8_REVERSE_JSARGS
int parameter_offset = (index + 1) * kSystemPointerSize;
#else
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment