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:
Leszek Swirski 2022-11-21 12:04:25 +01:00 committed by V8 LUCI CQ
parent a765588a42
commit 821c385e1e
5 changed files with 108 additions and 49 deletions

View File

@ -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();

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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;
}