Reland "[maglev] Support call speculation disabling"
This is a reland of commit 00db0fff8c
Fix missing update_feedback_count setting, and adding update feedback to
lazy deopt.
Original change's description:
> [maglev] Support call speculation disabling
>
> Add a FeedbackSource to DeoptInfo which allows the caller to specify
> that this deopt is part of call speculation, and that call speculation
> should be disabled for this call when the speculation fails. This is a
> mechanism to prevent deopt loops, also used by TurboFan.
>
> Bug: v8:7700
> Change-Id: I59b5db3956e074ec808b218c00ae85796455742e
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4030438
> Reviewed-by: Victor Gomes <victorgomes@chromium.org>
> Auto-Submit: Leszek Swirski <leszeks@chromium.org>
> Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#84332}
Bug: v8:7700
Change-Id: I3dee2108495776d37417982ad593f6daa460919e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4042661
Reviewed-by: Victor Gomes <victorgomes@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84385}
This commit is contained in:
parent
a765588a42
commit
821c385e1e
@ -1071,10 +1071,16 @@ class MaglevTranslationArrayBuilder {
|
||||
void BuildEagerDeopt(EagerDeoptInfo* deopt_info) {
|
||||
int frame_count = GetFrameCount(deopt_info->top_frame());
|
||||
int jsframe_count = frame_count;
|
||||
int update_feedback_count = 0;
|
||||
int update_feedback_count =
|
||||
deopt_info->feedback_to_update().IsValid() ? 1 : 0;
|
||||
deopt_info->set_translation_index(
|
||||
translation_array_builder_->BeginTranslation(frame_count, jsframe_count,
|
||||
update_feedback_count));
|
||||
if (deopt_info->feedback_to_update().IsValid()) {
|
||||
translation_array_builder_->AddUpdateFeedback(
|
||||
GetDeoptLiteral(*deopt_info->feedback_to_update().vector),
|
||||
deopt_info->feedback_to_update().index());
|
||||
}
|
||||
|
||||
const InputLocation* current_input_location = deopt_info->input_locations();
|
||||
BuildDeoptFrame(deopt_info->top_frame(), current_input_location);
|
||||
@ -1083,10 +1089,16 @@ class MaglevTranslationArrayBuilder {
|
||||
void BuildLazyDeopt(LazyDeoptInfo* deopt_info) {
|
||||
int frame_count = GetFrameCount(deopt_info->top_frame());
|
||||
int jsframe_count = frame_count;
|
||||
int update_feedback_count = 0;
|
||||
int update_feedback_count =
|
||||
deopt_info->feedback_to_update().IsValid() ? 1 : 0;
|
||||
deopt_info->set_translation_index(
|
||||
translation_array_builder_->BeginTranslation(frame_count, jsframe_count,
|
||||
update_feedback_count));
|
||||
if (deopt_info->feedback_to_update().IsValid()) {
|
||||
translation_array_builder_->AddUpdateFeedback(
|
||||
GetDeoptLiteral(*deopt_info->feedback_to_update().vector),
|
||||
deopt_info->feedback_to_update().index());
|
||||
}
|
||||
|
||||
const InputLocation* current_input_location = deopt_info->input_locations();
|
||||
|
||||
|
@ -183,6 +183,25 @@ class CallArguments {
|
||||
Mode mode_;
|
||||
};
|
||||
|
||||
class MaglevGraphBuilder::CallSpeculationScope {
|
||||
public:
|
||||
CallSpeculationScope(MaglevGraphBuilder* builder,
|
||||
compiler::FeedbackSource feedback_source)
|
||||
: builder_(builder) {
|
||||
DCHECK(!builder_->current_speculation_feedback_.IsValid());
|
||||
DCHECK_EQ(
|
||||
FeedbackNexus(feedback_source.vector, feedback_source.slot).kind(),
|
||||
FeedbackSlotKind::kCall);
|
||||
builder_->current_speculation_feedback_ = feedback_source;
|
||||
}
|
||||
~CallSpeculationScope() {
|
||||
builder_->current_speculation_feedback_ = compiler::FeedbackSource();
|
||||
}
|
||||
|
||||
private:
|
||||
MaglevGraphBuilder* builder_;
|
||||
};
|
||||
|
||||
MaglevGraphBuilder::MaglevGraphBuilder(LocalIsolate* local_isolate,
|
||||
MaglevCompilationUnit* compilation_unit,
|
||||
Graph* graph, MaglevGraphBuilder* parent)
|
||||
@ -2902,26 +2921,12 @@ ValueNode* MaglevGraphBuilder::TryBuildInlinedCall(
|
||||
ValueNode* MaglevGraphBuilder::TryReduceStringFromCharCode(
|
||||
compiler::JSFunctionRef target, CallArguments& args) {
|
||||
if (args.count() != 1) return nullptr;
|
||||
ValueNode* arg = args[0];
|
||||
// Bail out of the inlining if the known type is not Number. This prevents
|
||||
// GetTruncatedInt32FromNumber from deoptimizing.
|
||||
// TODO(leszeks): Add support for call feedback speculation.
|
||||
if (!CheckType(arg, NodeType::kNumber)) {
|
||||
return nullptr;
|
||||
}
|
||||
return AddNewNode<BuiltinStringFromCharCode>(
|
||||
{GetTruncatedInt32FromNumber(arg)});
|
||||
{GetTruncatedInt32FromNumber(args[0])});
|
||||
}
|
||||
|
||||
ValueNode* MaglevGraphBuilder::TryReduceStringPrototypeCharCodeAt(
|
||||
compiler::JSFunctionRef target, CallArguments& args) {
|
||||
// Bail out of the inlining if the known type is not Number or String. This
|
||||
// prevents GetInt32ElementIndex from deoptimizing.
|
||||
// TODO(leszeks): Add support for call feedback speculation.
|
||||
if (args.count() != 0 && !CheckType(args[0], NodeType::kNumber) &&
|
||||
!CheckType(args[0], NodeType::kString)) {
|
||||
return nullptr;
|
||||
}
|
||||
ValueNode* receiver = GetTaggedOrUndefined(args.receiver());
|
||||
ValueNode* index;
|
||||
if (args.count() == 0) {
|
||||
@ -3041,13 +3046,20 @@ ValueNode* MaglevGraphBuilder::TryReduceFunctionPrototypeCall(
|
||||
return BuildGenericCall(receiver, context, Call::TargetType::kAny, args);
|
||||
}
|
||||
|
||||
ValueNode* MaglevGraphBuilder::TryReduceBuiltin(compiler::JSFunctionRef target,
|
||||
CallArguments& args) {
|
||||
ValueNode* MaglevGraphBuilder::TryReduceBuiltin(
|
||||
compiler::JSFunctionRef target, CallArguments& args,
|
||||
const compiler::FeedbackSource& feedback_source,
|
||||
SpeculationMode speculation_mode) {
|
||||
if (args.mode() != CallArguments::kDefault) {
|
||||
// TODO(victorgomes): Maybe inline the spread stub? Or call known function
|
||||
// directly if arguments list is an array.
|
||||
return nullptr;
|
||||
}
|
||||
if (speculation_mode == SpeculationMode::kDisallowSpeculation) {
|
||||
// TODO(leszeks): Some builtins might be inlinable without speculation.
|
||||
return nullptr;
|
||||
}
|
||||
CallSpeculationScope speculate(this, feedback_source);
|
||||
if (!target.shared().HasBuiltinId()) return nullptr;
|
||||
switch (target.shared().builtin_id()) {
|
||||
#define CASE(Name) \
|
||||
@ -3158,8 +3170,10 @@ bool MaglevGraphBuilder::BuildCheckValue(ValueNode* node,
|
||||
return true;
|
||||
}
|
||||
|
||||
ValueNode* MaglevGraphBuilder::ReduceCall(compiler::ObjectRef object,
|
||||
CallArguments& args) {
|
||||
ValueNode* MaglevGraphBuilder::ReduceCall(
|
||||
compiler::ObjectRef object, CallArguments& args,
|
||||
const compiler::FeedbackSource& feedback_source,
|
||||
SpeculationMode speculation_mode) {
|
||||
if (!object.IsJSFunction()) {
|
||||
return BuildGenericCall(GetConstant(object), GetContext(),
|
||||
Call::TargetType::kAny, args);
|
||||
@ -3174,7 +3188,8 @@ ValueNode* MaglevGraphBuilder::ReduceCall(compiler::ObjectRef object,
|
||||
}
|
||||
|
||||
DCHECK(target.object()->IsCallable());
|
||||
if (ValueNode* result = TryReduceBuiltin(target, args)) {
|
||||
if (ValueNode* result =
|
||||
TryReduceBuiltin(target, args, feedback_source, speculation_mode)) {
|
||||
return result;
|
||||
}
|
||||
if (ValueNode* result = TryBuildCallKnownJSFunction(target, args)) {
|
||||
@ -3186,15 +3201,17 @@ ValueNode* MaglevGraphBuilder::ReduceCall(compiler::ObjectRef object,
|
||||
}
|
||||
|
||||
ValueNode* MaglevGraphBuilder::ReduceCallForTarget(
|
||||
ValueNode* target_node, compiler::JSFunctionRef target,
|
||||
CallArguments& args) {
|
||||
ValueNode* target_node, compiler::JSFunctionRef target, CallArguments& args,
|
||||
const compiler::FeedbackSource& feedback_source,
|
||||
SpeculationMode speculation_mode) {
|
||||
if (!BuildCheckValue(target_node, target)) return nullptr;
|
||||
return ReduceCall(target, args);
|
||||
return ReduceCall(target, args, feedback_source, speculation_mode);
|
||||
}
|
||||
|
||||
ValueNode* MaglevGraphBuilder::ReduceFunctionPrototypeApplyCallWithReceiver(
|
||||
ValueNode* target_node, compiler::JSFunctionRef receiver,
|
||||
CallArguments& args) {
|
||||
CallArguments& args, const compiler::FeedbackSource& feedback_source,
|
||||
SpeculationMode speculation_mode) {
|
||||
compiler::NativeContextRef native_context = broker()->target_native_context();
|
||||
if (!BuildCheckValue(target_node,
|
||||
native_context.function_prototype_apply())) {
|
||||
@ -3208,12 +3225,12 @@ ValueNode* MaglevGraphBuilder::ReduceFunctionPrototypeApplyCallWithReceiver(
|
||||
if (args.count() == 0) {
|
||||
// No need for spread.
|
||||
CallArguments empty_args(ConvertReceiverMode::kNullOrUndefined);
|
||||
call = ReduceCall(receiver, empty_args);
|
||||
call = ReduceCall(receiver, empty_args, feedback_source, speculation_mode);
|
||||
} else if (args.count() == 1 || IsNullValue(args[1]) ||
|
||||
IsUndefinedValue(args[1])) {
|
||||
// No need for spread. We have only the new receiver.
|
||||
CallArguments new_args(ConvertReceiverMode::kAny, {args[0]});
|
||||
call = ReduceCall(receiver, new_args);
|
||||
call = ReduceCall(receiver, new_args, feedback_source, speculation_mode);
|
||||
} else {
|
||||
// FunctionPrototypeApply only consider two arguments: the new receiver and
|
||||
// an array-like arguments_list. All others shall be ignored.
|
||||
@ -3225,10 +3242,11 @@ ValueNode* MaglevGraphBuilder::ReduceFunctionPrototypeApplyCallWithReceiver(
|
||||
// constant and unfold the arguments.
|
||||
CallArguments new_args(ConvertReceiverMode::kAny, {args[0], args[1]},
|
||||
CallArguments::kWithArrayLike);
|
||||
call = ReduceCall(receiver, new_args);
|
||||
call = ReduceCall(receiver, new_args, feedback_source, speculation_mode);
|
||||
} else {
|
||||
args.Truncate(2);
|
||||
call = ReduceCall(native_context.function_prototype_apply(), args);
|
||||
call = ReduceCall(native_context.function_prototype_apply(), args,
|
||||
feedback_source, speculation_mode);
|
||||
}
|
||||
}
|
||||
return call;
|
||||
@ -3251,13 +3269,15 @@ void MaglevGraphBuilder::BuildCall(ValueNode* target_node, CallArguments& args,
|
||||
compiler::JSFunctionRef function = call_feedback.target()->AsJSFunction();
|
||||
ValueNode* call;
|
||||
if (content == CallFeedbackContent::kTarget) {
|
||||
call = ReduceCallForTarget(target_node, function, args);
|
||||
call = ReduceCallForTarget(target_node, function, args, feedback_source,
|
||||
call_feedback.speculation_mode());
|
||||
} else {
|
||||
DCHECK_EQ(content, CallFeedbackContent::kReceiver);
|
||||
// We only collect receiver feedback for FunctionPrototypeApply.
|
||||
// See CollectCallFeedback in ic-callable.tq
|
||||
call = ReduceFunctionPrototypeApplyCallWithReceiver(target_node, function,
|
||||
args);
|
||||
call = ReduceFunctionPrototypeApplyCallWithReceiver(
|
||||
target_node, function, args, feedback_source,
|
||||
call_feedback.speculation_mode());
|
||||
}
|
||||
// If {call} is null, we hit an unconditional deopt.
|
||||
if (!call) return;
|
||||
@ -3851,7 +3871,8 @@ bool MaglevGraphBuilder::TryBuildFastInstanceOf(
|
||||
BuiltinContinuationDeoptFrame(
|
||||
Builtin::kToBooleanLazyDeoptContinuation, {}, GetContext(),
|
||||
zone()->New<InterpretedDeoptFrame>(
|
||||
call->lazy_deopt_info()->top_frame().as_interpreted())));
|
||||
call->lazy_deopt_info()->top_frame().as_interpreted())),
|
||||
call->lazy_deopt_info()->feedback_to_update());
|
||||
}
|
||||
|
||||
// TODO(v8:7700): Do we need to call ToBoolean here? If we have reduce the
|
||||
|
@ -103,6 +103,8 @@ class MaglevGraphBuilder {
|
||||
Graph* graph() const { return graph_; }
|
||||
|
||||
private:
|
||||
class CallSpeculationScope;
|
||||
|
||||
bool CheckType(ValueNode* node, NodeType type);
|
||||
NodeInfo* CreateInfoIfNot(ValueNode* node, NodeType type);
|
||||
bool EnsureType(ValueNode* node, NodeType type, NodeType* old = nullptr);
|
||||
@ -419,9 +421,11 @@ class MaglevGraphBuilder {
|
||||
NodeT* CreateNewNodeHelper(Args&&... args) {
|
||||
if constexpr (NodeT::kProperties.can_eager_deopt()) {
|
||||
return NodeBase::New<NodeT>(zone(), GetLatestCheckpointedFrame(),
|
||||
current_speculation_feedback_,
|
||||
std::forward<Args>(args)...);
|
||||
} else if constexpr (NodeT::kProperties.can_lazy_deopt()) {
|
||||
return NodeBase::New<NodeT>(zone(), GetDeoptFrameForLazyDeopt(),
|
||||
current_speculation_feedback_,
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return NodeBase::New<NodeT>(zone(), std::forward<Args>(args)...);
|
||||
@ -1151,7 +1155,9 @@ class MaglevGraphBuilder {
|
||||
CallNode* AddNewCallNode(const CallArguments& args, Args&&... extra_args);
|
||||
|
||||
ValueNode* TryReduceBuiltin(compiler::JSFunctionRef builtin_target,
|
||||
CallArguments& args);
|
||||
CallArguments& args,
|
||||
const compiler::FeedbackSource& feedback_source,
|
||||
SpeculationMode speculation_mode);
|
||||
ValueNode* TryBuildCallKnownJSFunction(compiler::JSFunctionRef function,
|
||||
CallArguments& args);
|
||||
ValueNode* TryBuildInlinedCall(compiler::JSFunctionRef function,
|
||||
@ -1161,13 +1167,19 @@ class MaglevGraphBuilder {
|
||||
const CallArguments& args,
|
||||
const compiler::FeedbackSource& feedback_source =
|
||||
compiler::FeedbackSource());
|
||||
ValueNode* ReduceCall(compiler::ObjectRef target, CallArguments& args);
|
||||
ValueNode* ReduceCallForTarget(ValueNode* target_node,
|
||||
compiler::JSFunctionRef target,
|
||||
CallArguments& args);
|
||||
ValueNode* ReduceCall(
|
||||
compiler::ObjectRef target, CallArguments& args,
|
||||
const compiler::FeedbackSource& feedback_source =
|
||||
compiler::FeedbackSource(),
|
||||
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation);
|
||||
ValueNode* ReduceCallForTarget(
|
||||
ValueNode* target_node, compiler::JSFunctionRef target,
|
||||
CallArguments& args, const compiler::FeedbackSource& feedback_source,
|
||||
SpeculationMode speculation_mode);
|
||||
ValueNode* ReduceFunctionPrototypeApplyCallWithReceiver(
|
||||
ValueNode* target_node, compiler::JSFunctionRef receiver,
|
||||
CallArguments& args);
|
||||
CallArguments& args, const compiler::FeedbackSource& feedback_source,
|
||||
SpeculationMode speculation_mode);
|
||||
void BuildCall(ValueNode* target_node, CallArguments& args,
|
||||
compiler::FeedbackSource& feedback_source);
|
||||
void BuildCallFromRegisterList(ConvertReceiverMode receiver_mode);
|
||||
@ -1434,6 +1446,7 @@ class MaglevGraphBuilder {
|
||||
MergePointInterpreterFrameState** merge_states_;
|
||||
|
||||
InterpreterFrameState current_interpreter_frame_;
|
||||
compiler::FeedbackSource current_speculation_feedback_;
|
||||
|
||||
struct HandlerTableEntry {
|
||||
int end;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "src/codegen/x64/register-x64.h"
|
||||
#include "src/common/globals.h"
|
||||
#include "src/compiler/backend/instruction.h"
|
||||
#include "src/compiler/feedback-source.h"
|
||||
#include "src/compiler/js-heap-broker.h"
|
||||
#include "src/deoptimizer/deoptimize-reason.h"
|
||||
#include "src/ic/handler-configuration.h"
|
||||
@ -259,8 +260,10 @@ size_t GetInputLocationsArraySize(const DeoptFrame& top_frame) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DeoptInfo::DeoptInfo(Zone* zone, DeoptFrame top_frame)
|
||||
DeoptInfo::DeoptInfo(Zone* zone, DeoptFrame top_frame,
|
||||
compiler::FeedbackSource feedback_to_update)
|
||||
: top_frame_(top_frame),
|
||||
feedback_to_update_(feedback_to_update),
|
||||
input_locations_(zone->NewArray<InputLocation>(
|
||||
GetInputLocationsArraySize(top_frame))) {
|
||||
// Initialise InputLocations so that they correctly don't have a next use id.
|
||||
|
@ -813,10 +813,14 @@ DeoptFrame::as_builtin_continuation() const {
|
||||
|
||||
class DeoptInfo {
|
||||
protected:
|
||||
DeoptInfo(Zone* zone, DeoptFrame top_frame);
|
||||
DeoptInfo(Zone* zone, DeoptFrame top_frame,
|
||||
compiler::FeedbackSource feedback_to_update);
|
||||
|
||||
public:
|
||||
const DeoptFrame& top_frame() const { return top_frame_; }
|
||||
const compiler::FeedbackSource& feedback_to_update() {
|
||||
return feedback_to_update_;
|
||||
}
|
||||
|
||||
InputLocation* input_locations() const { return input_locations_; }
|
||||
Label* deopt_entry_label() { return &deopt_entry_label_; }
|
||||
@ -826,6 +830,7 @@ class DeoptInfo {
|
||||
|
||||
private:
|
||||
const DeoptFrame top_frame_;
|
||||
const compiler::FeedbackSource feedback_to_update_;
|
||||
InputLocation* const input_locations_;
|
||||
Label deopt_entry_label_;
|
||||
int translation_index_ = -1;
|
||||
@ -839,8 +844,9 @@ struct RegisterSnapshot {
|
||||
|
||||
class EagerDeoptInfo : public DeoptInfo {
|
||||
public:
|
||||
EagerDeoptInfo(Zone* zone, DeoptFrame&& top_frame)
|
||||
: DeoptInfo(zone, std::move(top_frame)) {}
|
||||
EagerDeoptInfo(Zone* zone, DeoptFrame&& top_frame,
|
||||
compiler::FeedbackSource feedback_to_update)
|
||||
: DeoptInfo(zone, std::move(top_frame), feedback_to_update) {}
|
||||
|
||||
DeoptimizeReason reason() const { return reason_; }
|
||||
void set_reason(DeoptimizeReason reason) { reason_ = reason; }
|
||||
@ -851,8 +857,9 @@ class EagerDeoptInfo : public DeoptInfo {
|
||||
|
||||
class LazyDeoptInfo : public DeoptInfo {
|
||||
public:
|
||||
LazyDeoptInfo(Zone* zone, DeoptFrame&& top_frame)
|
||||
: DeoptInfo(zone, std::move(top_frame)) {}
|
||||
LazyDeoptInfo(Zone* zone, DeoptFrame&& top_frame,
|
||||
compiler::FeedbackSource feedback_to_update)
|
||||
: DeoptInfo(zone, std::move(top_frame), feedback_to_update) {}
|
||||
|
||||
interpreter::Register result_location() const { return result_location_; }
|
||||
int result_size() const { return result_size_; }
|
||||
@ -971,14 +978,17 @@ class NodeBase : public ZoneObject {
|
||||
}
|
||||
|
||||
template <class Derived, typename... Args>
|
||||
static Derived* New(Zone* zone, DeoptFrame&& deopt_frame, Args&&... args) {
|
||||
static Derived* New(Zone* zone, DeoptFrame&& deopt_frame,
|
||||
compiler::FeedbackSource feedback_to_update,
|
||||
Args&&... args) {
|
||||
Derived* node = New<Derived>(zone, std::forward<Args>(args)...);
|
||||
if constexpr (Derived::kProperties.can_eager_deopt()) {
|
||||
new (node->eager_deopt_info())
|
||||
EagerDeoptInfo(zone, std::move(deopt_frame));
|
||||
EagerDeoptInfo(zone, std::move(deopt_frame), feedback_to_update);
|
||||
} else {
|
||||
static_assert(Derived::kProperties.can_lazy_deopt());
|
||||
new (node->lazy_deopt_info()) LazyDeoptInfo(zone, std::move(deopt_frame));
|
||||
new (node->lazy_deopt_info())
|
||||
LazyDeoptInfo(zone, std::move(deopt_frame), feedback_to_update);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user