Commit 81642fa6 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[deoptimizer] Extract frame layout calculation into helper classes

The deoptimizer calculates frame layout based on the translation's
`height` field, together with additional data (e.g.: are we looking at
the topmost frame? what kind of deopt are we in?). The result is the
final deoptimized frame size in bytes, together with a bunch of
intermediate results such as the variable frame size (= without the
fixed-size portion).

In order to consider the deoptimized frame size in optimized stack
checks, we will need to calculate the frame layout during compilation
in addition to what we currently do during deoptimization. This CL
moves in that direction by extracting relevant parts of frame layout
calculation into classes that can be reused by both compiler and
deoptimizer.

These helpers will support both precise and conservative modes; the
deoptimizer will use the precise mode (since it has full information),
while the instruction selector will use the conservative mode.

Bug: v8:9534
Change-Id: I93d6c39f10d251733f4625d3cc161b2010652d02
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1760825
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63279}
parent 69d0eade
......@@ -2125,6 +2125,7 @@ v8_source_set("v8_base_without_compiler") {
"src/codegen/register-arch.h",
"src/codegen/register-configuration.cc",
"src/codegen/register-configuration.h",
"src/codegen/register.cc",
"src/codegen/register.h",
"src/codegen/reglist.h",
"src/codegen/reloc-info.cc",
......
......@@ -158,8 +158,7 @@ int Builtins::GetStackParameterCount(Name name) {
}
// static
Callable Builtins::CallableFor(Isolate* isolate, Name name) {
Handle<Code> code = isolate->builtins()->builtin_handle(name);
CallInterfaceDescriptor Builtins::CallInterfaceDescriptorFor(Name name) {
CallDescriptors::Key key;
switch (name) {
// This macro is deliberately crafted so as to emit very little code,
......@@ -176,12 +175,17 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) {
Builtins::Kind kind = Builtins::KindOf(name);
DCHECK_NE(BCH, kind);
if (kind == TFJ || kind == CPP) {
return Callable(code, JSTrampolineDescriptor{});
return JSTrampolineDescriptor{};
}
UNREACHABLE();
}
CallInterfaceDescriptor descriptor(key);
return Callable(code, descriptor);
return CallInterfaceDescriptor{key};
}
// static
Callable Builtins::CallableFor(Isolate* isolate, Name name) {
Handle<Code> code = isolate->builtins()->builtin_handle(name);
return Callable{code, CallInterfaceDescriptorFor(name)};
}
// static
......
......@@ -13,6 +13,7 @@ namespace v8 {
namespace internal {
class ByteArray;
class CallInterfaceDescriptor;
class Callable;
template <typename T>
class Handle;
......@@ -92,6 +93,7 @@ class Builtins {
V8_EXPORT_PRIVATE Code builtin(int index);
V8_EXPORT_PRIVATE Handle<Code> builtin_handle(int index);
static CallInterfaceDescriptor CallInterfaceDescriptorFor(Name name);
V8_EXPORT_PRIVATE static Callable CallableFor(Isolate* isolate, Name name);
static int GetStackParameterCount(Name name);
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/codegen/register.h"
#include "src/codegen/register-arch.h"
namespace v8 {
namespace internal {
bool ShouldPadArguments(int argument_count) {
return kPadArguments && (argument_count % 2 != 0);
}
} // namespace internal
} // namespace v8
......@@ -105,6 +105,9 @@ class RegisterBase {
int reg_code_;
};
// Whether padding is needed for the given stack argument count.
bool ShouldPadArguments(int argument_count);
template <typename RegType,
typename = decltype(RegisterName(std::declval<RegType>()))>
inline std::ostream& operator<<(std::ostream& os, RegType reg) {
......
......@@ -104,19 +104,16 @@ int CallDescriptor::GetStackParameterDelta(
int callee_slots_above_sp = GetFirstUnusedStackSlot();
int tail_caller_slots_above_sp = tail_caller->GetFirstUnusedStackSlot();
int stack_param_delta = callee_slots_above_sp - tail_caller_slots_above_sp;
if (kPadArguments) {
// Adjust stack delta when it is odd.
if (stack_param_delta % 2 != 0) {
if (callee_slots_above_sp % 2 != 0) {
// The delta is odd due to the callee - we will need to add one slot
// of padding.
++stack_param_delta;
} else {
// The delta is odd because of the caller. We already have one slot of
// padding that we can reuse for arguments, so we will need one fewer
// slot.
--stack_param_delta;
}
if (ShouldPadArguments(stack_param_delta)) {
if (callee_slots_above_sp % 2 != 0) {
// The delta is odd due to the callee - we will need to add one slot
// of padding.
++stack_param_delta;
} else {
// The delta is odd because of the caller. We already have one slot of
// padding that we can reuse for arguments, so we will need one fewer
// slot.
--stack_param_delta;
}
}
return stack_param_delta;
......
......@@ -6920,7 +6920,7 @@ CallDescriptor* GetWasmCallDescriptor(
wasm::kFpReturnRegisters);
int parameter_slots = params.NumStackSlots();
if (kPadArguments) parameter_slots = RoundUp(parameter_slots, 2);
if (ShouldPadArguments(parameter_slots)) parameter_slots++;
rets.SetStackOffset(parameter_slots);
......
......@@ -228,8 +228,6 @@ Float32 RegisterValues::GetFloatRegister(unsigned n) const {
static_cast<uint32_t>(double_registers_[n / 2].get_bits() >> kShift));
}
bool Deoptimizer::PadTopOfStackRegister() { return false; }
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
SetFrameSlot(offset, value);
}
......
......@@ -279,8 +279,6 @@ Float32 RegisterValues::GetFloatRegister(unsigned n) const {
static_cast<uint32_t>(double_registers_[n].get_bits()));
}
bool Deoptimizer::PadTopOfStackRegister() { return true; }
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
SetFrameSlot(offset, value);
}
......
This diff is collapsed.
......@@ -35,6 +35,8 @@ class TranslatedState;
class RegisterValues;
class MacroAssembler;
enum class BuiltinContinuationMode;
class TranslatedValue {
public:
// Allocation-less getter of the value.
......@@ -517,13 +519,6 @@ class Deoptimizer : public Malloced {
// kSupportsFixedDeoptExitSize is true.
static const int kDeoptExitSize;
enum class BuiltinContinuationMode {
STUB,
JAVASCRIPT,
JAVASCRIPT_WITH_CATCH,
JAVASCRIPT_HANDLE_EXCEPTION
};
private:
friend class FrameWriter;
void QueueValueForMaterialization(Address output_address, Object obj,
......@@ -546,8 +541,6 @@ class Deoptimizer : public Malloced {
void DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
int frame_index);
static bool BuiltinContinuationModeIsWithCatch(BuiltinContinuationMode mode);
static bool BuiltinContinuationModeIsJavaScript(BuiltinContinuationMode mode);
static Builtins::Name TrampolineForBuiltinContinuation(
BuiltinContinuationMode mode, bool must_handle_result);
......@@ -557,7 +550,6 @@ class Deoptimizer : public Malloced {
unsigned ComputeInputFrameAboveFpFixedSize() const;
unsigned ComputeInputFrameSize() const;
static unsigned ComputeInterpretedFixedSize(SharedFunctionInfo shared);
static unsigned ComputeIncomingArgumentSize(SharedFunctionInfo shared);
static unsigned ComputeOutgoingArgumentSize(Code code, unsigned bailout_id);
......@@ -568,14 +560,6 @@ class Deoptimizer : public Malloced {
static void MarkAllCodeForContext(NativeContext native_context);
static void DeoptimizeMarkedCodeForContext(NativeContext native_context);
// Some architectures need to push padding together with the TOS register
// in order to maintain stack alignment.
static bool PadTopOfStackRegister();
static int TopOfStackRegisterPaddingSlots() {
return PadTopOfStackRegister() ? 1 : 0;
}
// Searches the list of known deoptimizing code for a Code object
// containing the given address (which is supposedly faster than
// searching all code objects).
......@@ -690,7 +674,7 @@ class FrameDescription {
unsigned GetLastArgumentSlotOffset() {
int parameter_slots = parameter_count();
if (kPadArguments) parameter_slots = RoundUp(parameter_slots, 2);
if (ShouldPadArguments(parameter_slots)) parameter_slots++;
return GetFrameSize() - parameter_slots * kSystemPointerSize;
}
......
......@@ -204,8 +204,6 @@ Float32 RegisterValues::GetFloatRegister(unsigned n) const {
static_cast<uint32_t>(double_registers_[n].get_bits()));
}
bool Deoptimizer::PadTopOfStackRegister() { return false; }
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
SetFrameSlot(offset, value);
}
......
......@@ -223,8 +223,6 @@ Float32 RegisterValues::GetFloatRegister(unsigned n) const {
static_cast<uint32_t>(double_registers_[n].get_bits()));
}
bool Deoptimizer::PadTopOfStackRegister() { return false; }
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
SetFrameSlot(offset, value);
}
......
......@@ -223,8 +223,6 @@ Float32 RegisterValues::GetFloatRegister(unsigned n) const {
static_cast<uint32_t>(double_registers_[n].get_bits()));
}
bool Deoptimizer::PadTopOfStackRegister() { return false; }
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
SetFrameSlot(offset, value);
}
......
......@@ -224,8 +224,6 @@ Float32 RegisterValues::GetFloatRegister(unsigned n) const {
return Float32::FromBits(bit_cast<uint32_t>(float_val));
}
bool Deoptimizer::PadTopOfStackRegister() { return false; }
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
SetFrameSlot(offset, value);
}
......
......@@ -217,8 +217,6 @@ Float32 RegisterValues::GetFloatRegister(unsigned n) const {
static_cast<uint32_t>(double_registers_[n].get_bits() >> 32));
}
bool Deoptimizer::PadTopOfStackRegister() { return false; }
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
SetFrameSlot(offset, value);
}
......
......@@ -220,8 +220,6 @@ Float32 RegisterValues::GetFloatRegister(unsigned n) const {
static_cast<uint32_t>(double_registers_[n].get_bits()));
}
bool Deoptimizer::PadTopOfStackRegister() { return false; }
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
if (kPCOnStackSize == 2 * kSystemPointerSize) {
// Zero out the high-32 bit of PC for x32 port.
......
......@@ -8,6 +8,7 @@
#include <sstream>
#include "src/base/bits.h"
#include "src/codegen/interface-descriptors.h"
#include "src/codegen/macro-assembler.h"
#include "src/codegen/register-configuration.h"
#include "src/codegen/safepoint-table.h"
......@@ -2268,5 +2269,161 @@ InnerPointerToCodeCache::GetCacheEntry(Address inner_pointer) {
}
return entry;
}
// Frame layout helper class implementation.
// -------------------------------------------------------------------------
namespace {
int ArgumentPaddingSlots(int arg_count) {
return ShouldPadArguments(arg_count) ? 1 : 0;
}
// Some architectures need to push padding together with the TOS register
// in order to maintain stack alignment.
constexpr int TopOfStackRegisterPaddingSlots() { return kPadArguments ? 1 : 0; }
bool BuiltinContinuationModeIsWithCatch(BuiltinContinuationMode mode) {
switch (mode) {
case BuiltinContinuationMode::STUB:
case BuiltinContinuationMode::JAVASCRIPT:
return false;
case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH:
case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION:
return true;
}
UNREACHABLE();
}
} // namespace
InterpretedFrameInfo::InterpretedFrameInfo(int parameters_count_with_receiver,
int translation_height,
bool is_topmost,
FrameInfoKind frame_info_kind) {
const int locals_count = translation_height;
register_stack_slot_count_ =
InterpreterFrameConstants::RegisterStackSlotCount(locals_count);
static constexpr int kTheAccumulator = 1;
static constexpr int kTopOfStackPadding = TopOfStackRegisterPaddingSlots();
int maybe_additional_slots =
(is_topmost || frame_info_kind == FrameInfoKind::kConservative)
? (kTheAccumulator + kTopOfStackPadding)
: 0;
frame_size_in_bytes_without_fixed_ =
(register_stack_slot_count_ + maybe_additional_slots) *
kSystemPointerSize;
// The 'fixed' part of the frame consists of the incoming parameters and
// the part described by InterpreterFrameConstants. This will include
// argument padding, when needed.
const int parameter_padding_slots =
ArgumentPaddingSlots(parameters_count_with_receiver);
const int fixed_frame_size =
InterpreterFrameConstants::kFixedFrameSize +
(parameters_count_with_receiver + parameter_padding_slots) *
kSystemPointerSize;
frame_size_in_bytes_ = frame_size_in_bytes_without_fixed_ + fixed_frame_size;
}
ArgumentsAdaptorFrameInfo::ArgumentsAdaptorFrameInfo(int translation_height) {
// Note: This is according to the Translation's notion of 'parameters' which
// differs to that of the SharedFunctionInfo, e.g. by including the receiver.
const int parameters_count = translation_height;
frame_size_in_bytes_without_fixed_ =
(parameters_count + ArgumentPaddingSlots(parameters_count)) *
kSystemPointerSize;
frame_size_in_bytes_ = frame_size_in_bytes_without_fixed_ +
ArgumentsAdaptorFrameConstants::kFixedFrameSize;
}
ConstructStubFrameInfo::ConstructStubFrameInfo(int translation_height,
bool is_topmost,
FrameInfoKind frame_info_kind) {
// TODO(jgruber): Support conservative frame layout calculation.
DCHECK_EQ(frame_info_kind, FrameInfoKind::kPrecise);
// Note: This is according to the Translation's notion of 'parameters' which
// differs to that of the SharedFunctionInfo, e.g. by including the receiver.
const int parameters_count = translation_height;
// If the construct frame appears to be topmost we should ensure that the
// value of result register is preserved during continuation execution.
// We do this here by "pushing" the result of the constructor function to
// the top of the reconstructed stack and popping it in
// {Builtins::kNotifyDeoptimized}.
static constexpr int kTopOfStackPadding = TopOfStackRegisterPaddingSlots();
static constexpr int kTheResult = 1;
const int argument_padding = ArgumentPaddingSlots(parameters_count);
const int adjusted_height = is_topmost ? parameters_count + argument_padding +
kTheResult + kTopOfStackPadding
: parameters_count + argument_padding;
frame_size_in_bytes_without_fixed_ = adjusted_height * kSystemPointerSize;
frame_size_in_bytes_ = frame_size_in_bytes_without_fixed_ +
ConstructFrameConstants::kFixedFrameSize;
}
BuiltinContinuationFrameInfo::BuiltinContinuationFrameInfo(
int translation_height,
const CallInterfaceDescriptor& continuation_descriptor,
const RegisterConfiguration* register_config, bool is_topmost,
DeoptimizeKind deopt_kind, BuiltinContinuationMode continuation_mode,
FrameInfoKind frame_info_kind) {
// TODO(jgruber): Support conservative frame layout calculation.
DCHECK_EQ(frame_info_kind, FrameInfoKind::kPrecise);
// Note: This is according to the Translation's notion of 'parameters' which
// differs to that of the SharedFunctionInfo, e.g. by including the receiver.
const int parameters_count = translation_height;
frame_has_result_stack_slot_ =
!is_topmost || deopt_kind == DeoptimizeKind::kLazy;
const int result_slot_count = frame_has_result_stack_slot_ ? 1 : 0;
const int exception_slot_count =
(BuiltinContinuationModeIsWithCatch(continuation_mode) ? 1 : 0);
const int allocatable_register_count =
register_config->num_allocatable_general_registers();
const int padding_slot_count =
BuiltinContinuationFrameConstants::PaddingSlotCount(
allocatable_register_count);
const int register_parameter_count =
continuation_descriptor.GetRegisterParameterCount();
translated_stack_parameter_count_ =
parameters_count - register_parameter_count;
stack_parameter_count_ = translated_stack_parameter_count_ +
result_slot_count + exception_slot_count;
const int stack_param_pad_count =
ArgumentPaddingSlots(stack_parameter_count_);
// If the builtins frame appears to be topmost we should ensure that the
// value of result register is preserved during continuation execution.
// We do this here by "pushing" the result of callback function to the
// top of the reconstructed stack and popping it in
// {Builtins::kNotifyDeoptimized}.
static constexpr int kTopOfStackPadding = TopOfStackRegisterPaddingSlots();
static constexpr int kTheResult = 1;
const int push_result_count =
is_topmost ? kTheResult + kTopOfStackPadding : 0;
frame_size_in_bytes_ =
kSystemPointerSize * (stack_parameter_count_ + stack_param_pad_count +
allocatable_register_count + padding_slot_count +
push_result_count) +
BuiltinContinuationFrameConstants::kFixedFrameSize;
frame_size_in_bytes_above_fp_ =
kSystemPointerSize * (allocatable_register_count + padding_slot_count +
push_result_count) +
(BuiltinContinuationFrameConstants::kFixedFrameSize -
BuiltinContinuationFrameConstants::kFixedFrameSizeAboveFp);
}
} // namespace internal
} // namespace v8
......@@ -1313,6 +1313,140 @@ class SafeStackFrameIterator : public StackFrameIteratorBase {
ExternalCallbackScope* external_callback_scope_;
Address top_link_register_;
};
// Frame layout helper classes. Used by the deoptimizer and instruction
// selector.
// -------------------------------------------------------------------------
// How to calculate the frame layout information. Precise, when all information
// is available during deoptimization. Conservative, when an overapproximation
// is fine.
enum class FrameInfoKind {
kPrecise,
kConservative,
};
// Used by the deoptimizer. Corresponds to frame kinds:
enum class BuiltinContinuationMode {
STUB, // BuiltinContinuationFrame
JAVASCRIPT, // JavaScriptBuiltinContinuationFrame
JAVASCRIPT_WITH_CATCH, // JavaScriptBuiltinContinuationWithCatchFrame
JAVASCRIPT_HANDLE_EXCEPTION // JavaScriptBuiltinContinuationWithCatchFrame
};
class InterpretedFrameInfo {
public:
// Note: parameters_count includes the receiver, it's thus equal to
// `SharedFunctionInfo::internal_formal_parameter_count + 1`.
static InterpretedFrameInfo Precise(int parameters_count_with_receiver,
int translation_height, bool is_topmost) {
return {parameters_count_with_receiver, translation_height, is_topmost,
FrameInfoKind::kPrecise};
}
uint32_t register_stack_slot_count() const {
return register_stack_slot_count_;
}
uint32_t frame_size_in_bytes_without_fixed() const {
return frame_size_in_bytes_without_fixed_;
}
uint32_t frame_size_in_bytes() const { return frame_size_in_bytes_; }
private:
InterpretedFrameInfo(int parameters_count_with_receiver,
int translation_height, bool is_topmost,
FrameInfoKind frame_info_kind);
uint32_t register_stack_slot_count_;
uint32_t frame_size_in_bytes_without_fixed_;
uint32_t frame_size_in_bytes_;
};
class ArgumentsAdaptorFrameInfo {
public:
static ArgumentsAdaptorFrameInfo Precise(int translation_height) {
return ArgumentsAdaptorFrameInfo{translation_height};
}
uint32_t frame_size_in_bytes_without_fixed() const {
return frame_size_in_bytes_without_fixed_;
}
uint32_t frame_size_in_bytes() const { return frame_size_in_bytes_; }
private:
explicit ArgumentsAdaptorFrameInfo(int translation_height);
uint32_t frame_size_in_bytes_without_fixed_;
uint32_t frame_size_in_bytes_;
};
class ConstructStubFrameInfo {
public:
static ConstructStubFrameInfo Precise(int translation_height,
bool is_topmost) {
return {translation_height, is_topmost, FrameInfoKind::kPrecise};
}
uint32_t frame_size_in_bytes_without_fixed() const {
return frame_size_in_bytes_without_fixed_;
}
uint32_t frame_size_in_bytes() const { return frame_size_in_bytes_; }
private:
ConstructStubFrameInfo(int translation_height, bool is_topmost,
FrameInfoKind frame_info_kind);
uint32_t frame_size_in_bytes_without_fixed_;
uint32_t frame_size_in_bytes_;
};
// Used by BuiltinContinuationFrameInfo.
class CallInterfaceDescriptor;
class RegisterConfiguration;
class BuiltinContinuationFrameInfo {
public:
static BuiltinContinuationFrameInfo Precise(
int translation_height,
const CallInterfaceDescriptor& continuation_descriptor,
const RegisterConfiguration* register_config, bool is_topmost,
DeoptimizeKind deopt_kind, BuiltinContinuationMode continuation_mode) {
return {translation_height,
continuation_descriptor,
register_config,
is_topmost,
deopt_kind,
continuation_mode,
FrameInfoKind::kPrecise};
}
bool frame_has_result_stack_slot() const {
return frame_has_result_stack_slot_;
}
uint32_t translated_stack_parameter_count() const {
return translated_stack_parameter_count_;
}
uint32_t stack_parameter_count() const { return stack_parameter_count_; }
uint32_t frame_size_in_bytes() const { return frame_size_in_bytes_; }
uint32_t frame_size_in_bytes_above_fp() const {
return frame_size_in_bytes_above_fp_;
}
private:
BuiltinContinuationFrameInfo(
int translation_height,
const CallInterfaceDescriptor& continuation_descriptor,
const RegisterConfiguration* register_config, bool is_topmost,
DeoptimizeKind deopt_kind, BuiltinContinuationMode continuation_mode,
FrameInfoKind frame_info_kind);
bool frame_has_result_stack_slot_;
uint32_t translated_stack_parameter_count_;
uint32_t stack_parameter_count_;
uint32_t frame_size_in_bytes_;
uint32_t frame_size_in_bytes_above_fp_;
};
} // namespace internal
} // namespace v8
......
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