Commit 8a5cb3dc authored by Victor Gomes's avatar Victor Gomes Committed by Commit Bot

[arm64] Remove arguments adaptor frame

- It also fixes padding issues in the deoptimizer

Change-Id: Icac62892657830d067b7c21ff45b43ba58e350d9
Bug: v8:10201
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2498694
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@{#71090}
parent fbb3353c
......@@ -112,8 +112,8 @@ declare_args() {
# Disable arguments adaptor frame (sets -dV8_NO_ARGUMENTS_ADAPTOR).
v8_disable_arguments_adaptor =
v8_current_cpu == "x86" || v8_current_cpu == "x64" ||
v8_current_cpu == "mipsel" || v8_current_cpu == "mips64el" ||
v8_current_cpu == "arm"
v8_current_cpu == "arm" || v8_current_cpu == "arm64" ||
v8_current_cpu == "mipsel" || v8_current_cpu == "mips64el"
# Sets -dOBJECT_PRINT.
v8_enable_object_print = ""
......
This diff is collapsed.
......@@ -2079,23 +2079,148 @@ void TurboAssembler::PrepareForTailCall(Register callee_args_count,
Mov(sp, dst_reg);
}
void MacroAssembler::InvokePrologue(Register expected_parameter_count,
Register actual_parameter_count,
Label* done, InvokeFlag flag) {
Label regular_invoke;
void MacroAssembler::LoadStackLimit(Register destination, StackLimitKind kind) {
DCHECK(root_array_available());
Isolate* isolate = this->isolate();
ExternalReference limit =
kind == StackLimitKind::kRealStackLimit
? ExternalReference::address_of_real_jslimit(isolate)
: ExternalReference::address_of_jslimit(isolate);
DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit));
intptr_t offset =
TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit);
Ldr(destination, MemOperand(kRootRegister, offset));
}
// Check whether the expected and actual arguments count match. The registers
// are set up according to contract with ArgumentsAdaptorTrampoline:
void MacroAssembler::StackOverflowCheck(Register num_args,
Label* stack_overflow) {
UseScratchRegisterScope temps(this);
Register scratch = temps.AcquireX();
// Check the stack for overflow.
// We are not trying to catch interruptions (e.g. debug break and
// preemption) here, so the "real stack limit" is checked.
LoadStackLimit(scratch, StackLimitKind::kRealStackLimit);
// Make scratch the space we have left. The stack might already be overflowed
// here which will cause scratch to become negative.
Sub(scratch, sp, scratch);
// Check if the arguments will overflow the stack.
Cmp(scratch, Operand(num_args, LSL, kSystemPointerSizeLog2));
B(le, stack_overflow);
}
void MacroAssembler::InvokePrologue(Register formal_parameter_count,
Register actual_argument_count, Label* done,
InvokeFlag flag) {
// x0: actual arguments count.
// x1: function (passed through to callee).
// x2: expected arguments count.
// The code below is made a lot easier because the calling code already sets
// up actual and expected registers according to the contract.
DCHECK_EQ(actual_parameter_count, x0);
DCHECK_EQ(expected_parameter_count, x2);
// x3: new target
Label regular_invoke;
DCHECK_EQ(actual_argument_count, x0);
DCHECK_EQ(formal_parameter_count, x2);
#ifdef V8_NO_ARGUMENTS_ADAPTOR
// If the formal parameter count is equal to the adaptor sentinel, no need
// to push undefined value as arguments.
Cmp(formal_parameter_count, Operand(kDontAdaptArgumentsSentinel));
B(eq, &regular_invoke);
// If overapplication or if the actual argument count is equal to the
// formal parameter count, no need to push extra undefined values.
Register extra_argument_count = x2;
Subs(extra_argument_count, formal_parameter_count, actual_argument_count);
B(le, &regular_invoke);
// The stack pointer in arm64 needs to be 16-byte aligned. We might need to
// (1) add an extra padding or (2) remove (re-use) the extra padding already
// in the stack. Let {slots_to_copy} be the number of slots (arguments) to
// move up in the stack and let {slots_to_claim} be the number of extra stack
// slots to claim.
Label even_extra_count, skip_move;
Register slots_to_copy = x4;
Register slots_to_claim = x5;
Add(slots_to_copy, actual_argument_count, 1); // Copy with receiver.
Mov(slots_to_claim, extra_argument_count);
Tbz(extra_argument_count, 0, &even_extra_count);
// Calculate {slots_to_claim} when {extra_argument_count} is odd.
// If {actual_argument_count} is even, we need one extra padding slot
// {slots_to_claim = extra_argument_count + 1}.
// If {actual_argument_count} is odd, we know that the
// original arguments will have a padding slot that we can reuse
// {slots_to_claim = extra_argument_count - 1}.
{
Register scratch = x11;
Add(slots_to_claim, extra_argument_count, 1);
And(scratch, actual_argument_count, 1);
Eor(scratch, scratch, 1);
Sub(slots_to_claim, slots_to_claim, Operand(scratch, LSL, 1));
}
Bind(&even_extra_count);
Cbz(slots_to_claim, &skip_move);
Label stack_overflow;
StackOverflowCheck(slots_to_claim, &stack_overflow);
Claim(slots_to_claim);
// Move the arguments already in the stack including the receiver.
{
Register src = x6;
Register dst = x7;
SlotAddress(src, slots_to_claim);
SlotAddress(dst, 0);
CopyDoubleWords(dst, src, slots_to_copy);
}
Bind(&skip_move);
Register actual_argument_with_receiver = x4;
Register pointer_next_value = x5;
Add(actual_argument_with_receiver, actual_argument_count,
1); // {slots_to_copy} was scratched.
// Copy extra arguments as undefined values.
{
Label loop;
Register undefined_value = x6;
Register count = x7;
LoadRoot(undefined_value, RootIndex::kUndefinedValue);
SlotAddress(pointer_next_value, actual_argument_with_receiver);
Mov(count, extra_argument_count);
Bind(&loop);
Str(undefined_value,
MemOperand(pointer_next_value, kSystemPointerSize, PostIndex));
Subs(count, count, 1);
Cbnz(count, &loop);
}
// Set padding if needed.
{
Label skip;
Register total_args_slots = x4;
Add(total_args_slots, actual_argument_with_receiver, extra_argument_count);
Tbz(total_args_slots, 0, &skip);
Str(padreg, MemOperand(pointer_next_value));
Bind(&skip);
}
B(&regular_invoke);
bind(&stack_overflow);
{
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
CallRuntime(Runtime::kThrowStackOverflow);
Unreachable();
}
#else
// Check whether the expected and actual arguments count match. The registers
// are set up according to contract with ArgumentsAdaptorTrampoline.ct.
// If actual == expected perform a regular invocation.
Cmp(expected_parameter_count, actual_parameter_count);
Cmp(formal_parameter_count, actual_argument_count);
B(eq, &regular_invoke);
// The argument counts mismatch, generate a call to the argument adaptor.
......@@ -2108,6 +2233,7 @@ void MacroAssembler::InvokePrologue(Register expected_parameter_count,
} else {
Jump(adaptor, RelocInfo::CODE_TARGET);
}
#endif
Bind(&regular_invoke);
}
......
......@@ -145,6 +145,10 @@ enum PreShiftImmMode {
kAnyShift // Allow any pre-shift.
};
// TODO(victorgomes): Move definition to macro-assembler.h, once all other
// platforms are updated.
enum class StackLimitKind { kInterruptStackLimit, kRealStackLimit };
class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
public:
using TurboAssemblerBase::TurboAssemblerBase;
......@@ -1979,6 +1983,11 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
void DecrementCounter(StatsCounter* counter, int value, Register scratch1,
Register scratch2);
// ---------------------------------------------------------------------------
// Stack limit utilities
void LoadStackLimit(Register destination, StackLimitKind kind);
void StackOverflowCheck(Register num_args, Label* stack_overflow);
// ---------------------------------------------------------------------------
// Garbage collector support (GC).
......
......@@ -3093,11 +3093,10 @@ void CodeGenerator::AssembleConstructFrame() {
}
}
void CodeGenerator::AssembleReturn(InstructionOperand* pop) {
void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
auto call_descriptor = linkage()->GetIncomingDescriptor();
const int returns = RoundUp(frame()->GetReturnSlotCount(), 2);
if (returns != 0) {
__ Drop(returns);
}
......@@ -3114,35 +3113,78 @@ void CodeGenerator::AssembleReturn(InstructionOperand* pop) {
unwinding_info_writer_.MarkBlockWillExit();
// We might need x3 for scratch.
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & x3.bit());
const int parameter_count =
static_cast<int>(call_descriptor->StackParameterCount());
Arm64OperandConverter g(this, nullptr);
int pop_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), Operand(0));
__ Assert(eq, AbortReason::kUnexpectedAdditionalPopValue);
}
}
Register argc_reg = x3;
#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 unless they have an variable
// number of stack slot pops.
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()) {
__ B(&return_label_);
return;
} else {
__ Bind(&return_label_);
AssembleDeconstructFrame();
}
} else {
AssembleDeconstructFrame();
}
if (drop_jsargs) {
// Get the actual argument count.
__ Ldr(argc_reg, MemOperand(fp, StandardFrameConstants::kArgCOffset));
}
AssembleDeconstructFrame();
}
if (pop->IsImmediate()) {
pop_count += g.ToConstant(pop).ToInt32();
__ DropArguments(pop_count);
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).
Label argc_reg_has_final_count;
__ Add(argc_reg, argc_reg, 1); // Consider the receiver.
if (parameter_count > 1) {
__ Cmp(argc_reg, Operand(parameter_count));
__ B(&argc_reg_has_final_count, ge);
__ Mov(argc_reg, Operand(parameter_count));
__ Bind(&argc_reg_has_final_count);
}
__ DropArguments(argc_reg);
} else if (additional_pop_count->IsImmediate()) {
int additional_count = g.ToConstant(additional_pop_count).ToInt32();
__ DropArguments(parameter_count + additional_count);
} else if (parameter_count == 0) {
__ DropArguments(g.ToRegister(additional_pop_count));
} else {
Register pop_reg = g.ToRegister(pop);
__ Add(pop_reg, pop_reg, pop_count);
__ DropArguments(pop_reg);
// {additional_pop_count} is guaranteed to be zero if {parameter_count !=
// 0}. Check RawMachineAssembler::PopAndReturn.
__ DropArguments(parameter_count);
}
__ AssertSpAligned();
__ Ret();
}
......
......@@ -962,9 +962,22 @@ void Deoptimizer::DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
goto_catch_handler ? catch_handler_pc_offset_ : real_bytecode_offset;
const int parameters_count = InternalFormalParameterCountWithReceiver(shared);
#ifdef V8_NO_ARGUMENTS_ADAPTOR
// If this is the bottom most frame or the previous frame was the arguments
// adaptor fake frame, then we already have extra arguments in the stack
// (including any extra padding). Therefore we should not try to add any
// padding.
bool should_pad_arguments =
!is_bottommost && (translated_state_.frames()[frame_index - 1]).kind() !=
TranslatedFrame::kArgumentsAdaptor;
#else
bool should_pad_arguments = true;
#endif
const int locals_count = translated_frame->height();
InterpretedFrameInfo frame_info =
InterpretedFrameInfo::Precise(parameters_count, locals_count, is_topmost);
InterpretedFrameInfo frame_info = InterpretedFrameInfo::Precise(
parameters_count, locals_count, is_topmost, should_pad_arguments);
const uint32_t output_frame_size = frame_info.frame_size_in_bytes();
TranslatedFrame::iterator function_iterator = value_iterator++;
......@@ -996,9 +1009,10 @@ void Deoptimizer::DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
// Compute the incoming parameter translation.
ReadOnlyRoots roots(isolate());
if (ShouldPadArguments(parameters_count)) {
if (should_pad_arguments && 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) {
......@@ -1008,7 +1022,7 @@ void Deoptimizer::DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
}
frame_writer.PushStackJSArguments(value_iterator, parameters_count);
DCHECK_EQ(output_frame->GetLastArgumentSlotOffset(),
DCHECK_EQ(output_frame->GetLastArgumentSlotOffset(should_pad_arguments),
frame_writer.top_offset());
if (verbose_tracing_enabled()) {
PrintF(trace_scope()->file(), " -------------------------\n");
......@@ -1250,9 +1264,13 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(
translated_frame->raw_shared_info().internal_formal_parameter_count();
const int extra_argument_count =
argument_count_without_receiver - formal_parameter_count;
// The number of pushed arguments is the maximum of the actual argument count
// and the formal parameter count + the receiver.
const bool should_pad_args = ShouldPadArguments(
std::max(argument_count_without_receiver, formal_parameter_count) + 1);
const int output_frame_size =
std::max(0, extra_argument_count * kSystemPointerSize);
std::max(0, extra_argument_count * kSystemPointerSize) +
(should_pad_args ? kSystemPointerSize : 0);
if (verbose_tracing_enabled()) {
PrintF(trace_scope_->file(),
" translating arguments adaptor => variable_size=%d\n",
......@@ -1272,14 +1290,14 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(
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());
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");
}
ReadOnlyRoots roots(isolate());
if (should_pad_args) {
frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
}
if (extra_argument_count > 0) {
// 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
......@@ -2015,7 +2033,9 @@ unsigned Deoptimizer::ComputeInputFrameSize() const {
// static
unsigned Deoptimizer::ComputeIncomingArgumentSize(SharedFunctionInfo shared) {
int parameter_slots = InternalFormalParameterCountWithReceiver(shared);
#ifndef V8_NO_ARGUMENTS_ADAPTOR
if (ShouldPadArguments(parameter_slots)) parameter_slots++;
#endif
return parameter_slots * kSystemPointerSize;
}
......
......@@ -712,15 +712,23 @@ class FrameDescription {
return *GetFrameSlotPointer(offset);
}
unsigned GetLastArgumentSlotOffset() {
unsigned GetLastArgumentSlotOffset(bool pad_arguments = true) {
int parameter_slots = parameter_count();
if (ShouldPadArguments(parameter_slots)) parameter_slots++;
if (pad_arguments && ShouldPadArguments(parameter_slots)) parameter_slots++;
return GetFrameSize() - parameter_slots * kSystemPointerSize;
}
Address GetFramePointerAddress() {
int fp_offset =
GetLastArgumentSlotOffset() - StandardFrameConstants::kCallerSPOffset;
#ifdef V8_NO_ARGUMENTS_ADAPTOR
// We should not pad arguments in the bottom frame, since this
// already contain a padding if necessary and it might contain
// extra arguments (actual argument count > parameter count).
const bool pad_arguments_bottom_frame = false;
#else
const bool pad_arguments_bottom_frame = true;
#endif
int fp_offset = GetLastArgumentSlotOffset(pad_arguments_bottom_frame) -
StandardFrameConstants::kCallerSPOffset;
return reinterpret_cast<Address>(GetFrameSlotPointer(fp_offset));
}
......
......@@ -2246,7 +2246,7 @@ bool BuiltinContinuationModeIsWithCatch(BuiltinContinuationMode mode) {
InterpretedFrameInfo::InterpretedFrameInfo(int parameters_count_with_receiver,
int translation_height,
bool is_topmost,
bool is_topmost, bool pad_arguments,
FrameInfoKind frame_info_kind) {
const int locals_count = translation_height;
......@@ -2267,7 +2267,7 @@ InterpretedFrameInfo::InterpretedFrameInfo(int parameters_count_with_receiver,
// the part described by InterpreterFrameConstants. This will include
// argument padding, when needed.
const int parameter_padding_slots =
ArgumentPaddingSlots(parameters_count_with_receiver);
pad_arguments ? ArgumentPaddingSlots(parameters_count_with_receiver) : 0;
const int fixed_frame_size =
InterpreterFrameConstants::kFixedFrameSize +
(parameters_count_with_receiver + parameter_padding_slots) *
......
......@@ -1334,14 +1334,15 @@ enum class BuiltinContinuationMode {
class InterpretedFrameInfo {
public:
static InterpretedFrameInfo Precise(int parameters_count_with_receiver,
int translation_height, bool is_topmost) {
int translation_height, bool is_topmost,
bool pad_arguments) {
return {parameters_count_with_receiver, translation_height, is_topmost,
FrameInfoKind::kPrecise};
pad_arguments, FrameInfoKind::kPrecise};
}
static InterpretedFrameInfo Conservative(int parameters_count_with_receiver,
int locals_count) {
return {parameters_count_with_receiver, locals_count, false,
return {parameters_count_with_receiver, locals_count, false, true,
FrameInfoKind::kConservative};
}
......@@ -1356,7 +1357,7 @@ class InterpretedFrameInfo {
private:
InterpretedFrameInfo(int parameters_count_with_receiver,
int translation_height, bool is_topmost,
FrameInfoKind frame_info_kind);
bool pad_arguments, FrameInfoKind frame_info_kind);
uint32_t register_stack_slot_count_;
uint32_t frame_size_in_bytes_without_fixed_;
......
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