Commit a63f045c authored by Georgia Kouveli's avatar Georgia Kouveli Committed by Commit Bot

[arm64] Update BuiltinContinuation frames for jssp alignment.

Adds some necessary padding to ensure the frame is 16-byte aligned.
We don't yet consider the bailout state, which will be handled separately.

This patch also improves the code generated for ContinueTo*Builtin* stubs.

Finally, it adds a test that checks the return value for Array.map in
the case where a LAZY deopt results in a topmost builtin continuation
frame - this is easy to break if the padding for the result is done
incorrectly in NotifyBuiltinContinuation, but was not detected by existing
tests.

Bug: v8:6644
Change-Id: Id1a294950cdf535e2bfdb0ed27c67f077ec34f8a
Reviewed-on: https://chromium-review.googlesource.com/704835
Commit-Queue: Georgia Kouveli <georgia.kouveli@arm.com>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48465}
parent a7abde7c
...@@ -24,6 +24,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) { ...@@ -24,6 +24,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) {
return register_count; return register_count;
} }
int BuiltinContinuationFrameConstants::PaddingSlotCount(int register_count) {
USE(register_count);
return 0;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -24,6 +24,14 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) { ...@@ -24,6 +24,14 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) {
return RoundUp(register_count, 2); return RoundUp(register_count, 2);
} }
int BuiltinContinuationFrameConstants::PaddingSlotCount(int register_count) {
// Round the total slot count up to a multiple of two, to make the frame a
// multiple of 16 bytes.
int slot_count = kFixedSlotCount + register_count;
int rounded_slot_count = RoundUp(slot_count, 2);
return rounded_slot_count - slot_count;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -1511,10 +1511,10 @@ void Builtins::Generate_NotifyBuiltinContinuation(MacroAssembler* masm) { ...@@ -1511,10 +1511,10 @@ void Builtins::Generate_NotifyBuiltinContinuation(MacroAssembler* masm) {
{ {
FrameScope scope(masm, StackFrame::INTERNAL); FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve possible return result from lazy deopt. // Preserve possible return result from lazy deopt.
__ Push(x0); __ Push(x0, padreg);
// Pass the function and deoptimization type to the runtime system. // Pass the function and deoptimization type to the runtime system.
__ CallRuntime(Runtime::kNotifyStubFailure, false); __ CallRuntime(Runtime::kNotifyStubFailure, false);
__ Pop(x0); __ Pop(padreg, x0);
} }
// Jump to the ContinueToBuiltin stub. Deoptimizer::EntryGenerator::Generate // Jump to the ContinueToBuiltin stub. Deoptimizer::EntryGenerator::Generate
...@@ -1528,30 +1528,55 @@ void Generate_ContinueToBuiltinHelper(MacroAssembler* masm, ...@@ -1528,30 +1528,55 @@ void Generate_ContinueToBuiltinHelper(MacroAssembler* masm,
bool with_result) { bool with_result) {
const RegisterConfiguration* config(RegisterConfiguration::Default()); const RegisterConfiguration* config(RegisterConfiguration::Default());
int allocatable_register_count = config->num_allocatable_general_registers(); int allocatable_register_count = config->num_allocatable_general_registers();
int frame_size = BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp +
(allocatable_register_count +
BuiltinContinuationFrameConstants::PaddingSlotCount(
allocatable_register_count)) *
kPointerSize;
// Set up frame pointer.
__ Add(fp, jssp, frame_size);
if (with_result) { if (with_result) {
// Overwrite the hole inserted by the deoptimizer with the return value from // Overwrite the hole inserted by the deoptimizer with the return value from
// the LAZY deopt point. // the LAZY deopt point.
__ Str(x0, MemOperand( __ Str(x0,
jssp, MemOperand(fp, BuiltinContinuationFrameConstants::kCallerSPOffset));
config->num_allocatable_general_registers() * kPointerSize + }
BuiltinContinuationFrameConstants::kFixedFrameSize));
} // Restore registers in pairs.
for (int i = allocatable_register_count - 1; i >= 0; --i) { int offset = -BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp -
int code = config->GetAllocatableGeneralCode(i); allocatable_register_count * kPointerSize;
__ Pop(Register::from_code(code)); for (int i = allocatable_register_count - 1; i > 0; i -= 2) {
if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) { int code1 = config->GetAllocatableGeneralCode(i);
__ SmiUntag(Register::from_code(code)); int code2 = config->GetAllocatableGeneralCode(i - 1);
} Register reg1 = Register::from_code(code1);
} Register reg2 = Register::from_code(code2);
__ ldr(fp, __ Ldp(reg1, reg2, MemOperand(fp, offset));
MemOperand(jssp, offset += 2 * kPointerSize;
BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp)); }
__ Pop(ip0);
__ Add(jssp, jssp, // Restore first register separately, if number of registers is odd.
Operand(BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp)); if (allocatable_register_count % 2 != 0) {
__ Pop(lr); int code = config->GetAllocatableGeneralCode(0);
__ Add(ip0, ip0, Operand(Code::kHeaderSize - kHeapObjectTag)); __ Ldr(Register::from_code(code), MemOperand(fp, offset));
__ Br(ip0); }
if (java_script_builtin) __ SmiUntag(kJavaScriptCallArgCountRegister);
// Load builtin object.
UseScratchRegisterScope temps(masm);
Register builtin = temps.AcquireX();
__ Ldr(builtin,
MemOperand(fp, BuiltinContinuationFrameConstants::kBuiltinOffset));
// Restore fp, lr.
__ Mov(__ StackPointer(), fp);
__ Pop(fp, lr);
// Call builtin.
__ Add(builtin, builtin, Code::kHeaderSize - kHeapObjectTag);
__ Br(builtin);
} }
} // namespace } // namespace
......
...@@ -1581,6 +1581,9 @@ void Deoptimizer::DoComputeBuiltinContinuation( ...@@ -1581,6 +1581,9 @@ void Deoptimizer::DoComputeBuiltinContinuation(
const RegisterConfiguration* config(RegisterConfiguration::Default()); const RegisterConfiguration* config(RegisterConfiguration::Default());
int allocatable_register_count = config->num_allocatable_general_registers(); int allocatable_register_count = config->num_allocatable_general_registers();
int padding_slot_count = BuiltinContinuationFrameConstants::PaddingSlotCount(
allocatable_register_count);
int register_parameter_count = int register_parameter_count =
continuation_descriptor.GetRegisterParameterCount(); continuation_descriptor.GetRegisterParameterCount();
// Make sure to account for the context by removing it from the register // Make sure to account for the context by removing it from the register
...@@ -1588,8 +1591,9 @@ void Deoptimizer::DoComputeBuiltinContinuation( ...@@ -1588,8 +1591,9 @@ void Deoptimizer::DoComputeBuiltinContinuation(
int stack_param_count = height_in_words - register_parameter_count - 1; int stack_param_count = height_in_words - register_parameter_count - 1;
if (must_handle_result) stack_param_count++; if (must_handle_result) stack_param_count++;
int output_frame_size = int output_frame_size =
kPointerSize * (stack_param_count + allocatable_register_count) + kPointerSize * (stack_param_count + allocatable_register_count +
TYPED_FRAME_SIZE(2); // For destination builtin code and registers padding_slot_count) +
BuiltinContinuationFrameConstants::kFixedFrameSize;
// Validate types of parameters. They must all be tagged except for argc for // Validate types of parameters. They must all be tagged except for argc for
// JS builtins. // JS builtins.
...@@ -1633,7 +1637,9 @@ void Deoptimizer::DoComputeBuiltinContinuation( ...@@ -1633,7 +1637,9 @@ void Deoptimizer::DoComputeBuiltinContinuation(
} }
output_frame->SetTop(top_address); output_frame->SetTop(top_address);
// Get the possible JSFunction for the case that // Get the possible JSFunction for the case that this is a
// JavaScriptBuiltinContinuationFrame, which needs the JSFunction pointer
// like a normal JavaScriptFrame.
intptr_t maybe_function = intptr_t maybe_function =
reinterpret_cast<intptr_t>(value_iterator->GetRawValue()); reinterpret_cast<intptr_t>(value_iterator->GetRawValue());
++input_index; ++input_index;
...@@ -1799,6 +1805,14 @@ void Deoptimizer::DoComputeBuiltinContinuation( ...@@ -1799,6 +1805,14 @@ void Deoptimizer::DoComputeBuiltinContinuation(
Register fp_reg = JavaScriptFrame::fp_register(); Register fp_reg = JavaScriptFrame::fp_register();
output_frame->SetRegister(fp_reg.code(), output_[frame_index - 1]->GetFp()); output_frame->SetRegister(fp_reg.code(), output_[frame_index - 1]->GetFp());
// Some architectures must pad the stack frame with extra stack slots
// to ensure the stack frame is aligned.
for (int i = 0; i < padding_slot_count; ++i) {
output_frame_offset -= kPointerSize;
WriteValueToOutput(isolate()->heap()->the_hole_value(), 0, frame_index,
output_frame_offset, "padding ");
}
Code* continue_to_builtin = Code* continue_to_builtin =
java_script_builtin java_script_builtin
? (must_handle_result ? (must_handle_result
......
...@@ -251,8 +251,16 @@ class BuiltinContinuationFrameConstants : public TypedFrameConstants { ...@@ -251,8 +251,16 @@ class BuiltinContinuationFrameConstants : public TypedFrameConstants {
// FP-relative. // FP-relative.
static const int kFunctionOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0); static const int kFunctionOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
static const int kBuiltinOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1); static const int kBuiltinOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
// The argument count is in the first allocatable register, stored below the
// fixed part of the frame and therefore is not part of the fixed frame size.
static const int kArgCOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2); static const int kArgCOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2);
DEFINE_TYPED_FRAME_SIZES(2); DEFINE_TYPED_FRAME_SIZES(2);
// Returns the number of padding stack slots needed when we have
// 'register_count' register slots.
// This is needed on some architectures to ensure the stack pointer is
// aligned.
static int PaddingSlotCount(int register_count);
}; };
// Behaves like an exit frame but with target and new target args. // Behaves like an exit frame but with target and new target args.
......
...@@ -1119,6 +1119,10 @@ int JavaScriptFrame::ComputeParametersCount() const { ...@@ -1119,6 +1119,10 @@ int JavaScriptFrame::ComputeParametersCount() const {
} }
int JavaScriptBuiltinContinuationFrame::ComputeParametersCount() const { int JavaScriptBuiltinContinuationFrame::ComputeParametersCount() const {
// Assert that the first allocatable register is also the argument count
// register.
DCHECK_EQ(RegisterConfiguration::Default()->GetAllocatableGeneralCode(0),
kJavaScriptCallArgCountRegister.code());
Object* argc_object = Object* argc_object =
Memory::Object_at(fp() + BuiltinContinuationFrameConstants::kArgCOffset); Memory::Object_at(fp() + BuiltinContinuationFrameConstants::kArgCOffset);
return Smi::ToInt(argc_object); return Smi::ToInt(argc_object);
......
...@@ -22,6 +22,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) { ...@@ -22,6 +22,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) {
return register_count; return register_count;
} }
int BuiltinContinuationFrameConstants::PaddingSlotCount(int register_count) {
USE(register_count);
return 0;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -22,6 +22,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) { ...@@ -22,6 +22,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) {
return register_count; return register_count;
} }
int BuiltinContinuationFrameConstants::PaddingSlotCount(int register_count) {
USE(register_count);
return 0;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -22,6 +22,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) { ...@@ -22,6 +22,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) {
return register_count; return register_count;
} }
int BuiltinContinuationFrameConstants::PaddingSlotCount(int register_count) {
USE(register_count);
return 0;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -27,6 +27,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) { ...@@ -27,6 +27,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) {
return register_count; return register_count;
} }
int BuiltinContinuationFrameConstants::PaddingSlotCount(int register_count) {
USE(register_count);
return 0;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -24,6 +24,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) { ...@@ -24,6 +24,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) {
return register_count; return register_count;
} }
int BuiltinContinuationFrameConstants::PaddingSlotCount(int register_count) {
USE(register_count);
return 0;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -22,6 +22,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) { ...@@ -22,6 +22,11 @@ int InterpreterFrameConstants::RegisterStackSlotCount(int register_count) {
return register_count; return register_count;
} }
int BuiltinContinuationFrameConstants::PaddingSlotCount(int register_count) {
USE(register_count);
return 0;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -101,6 +101,27 @@ var c = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]; ...@@ -101,6 +101,27 @@ var c = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
lazyDeopt(); lazyDeopt();
})(); })();
// Escape analyzed array where callback function isn't inlined, forcing a lazy
// deopt. Check that the result of the callback function is passed correctly
// to the lazy deopt and that the final result of map is as expected.
(function() {
var lazyDeopt = function(deopt) {
var b = [1,2,3];
var callback = function(v,i,o) {
if (i == 1 && deopt) {
%DeoptimizeFunction(lazyDeopt);
}
return 2 * v;
};
%NeverOptimizeFunction(callback);
return b.map(callback);
}
assertEquals([2,4,6], lazyDeopt());
assertEquals([2,4,6], lazyDeopt());
%OptimizeFunctionOnNextCall(lazyDeopt);
assertEquals([2,4,6], lazyDeopt(true));
})();
// Lazy deopt from runtime call from inlined callback function. // Lazy deopt from runtime call from inlined callback function.
(function() { (function() {
var result = 0; var result = 0;
......
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