Preserve feedback and speculation mode for JSCall

Changing the target of JSCall nodes (e.g. while lowering higher order calls)
now preserves feedback and speculation mode to allow further (speculative)
optimizations. A flag is introduced to mark feedback unrelated to the call
target after such a transformation. This flag is used to prevent access to
the feedback without the need to invalidate it.

Bug: v8:9702
Change-Id: I311d3a4b1b22d6f65e5837a23b0b7585c8d75eed
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1844788
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64733}
This commit is contained in:
Nico Hartmann 2019-10-31 16:27:05 +01:00 committed by Commit Bot
parent 0ec75c9173
commit 149e4935ba
12 changed files with 437 additions and 101 deletions

View File

@ -2231,8 +2231,9 @@ void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode,
FeedbackSource feedback = CreateFeedbackSource(slot_id);
CallFrequency frequency = ComputeCallFrequency(slot_id);
SpeculationMode speculation_mode = GetSpeculationMode(slot_id);
const Operator* op = javascript()->Call(arg_count, frequency, feedback,
receiver_mode, speculation_mode);
const Operator* op =
javascript()->Call(arg_count, frequency, feedback, receiver_mode,
speculation_mode, CallFeedbackRelation::kRelated);
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
op, args, static_cast<int>(arg_count), feedback.slot);
@ -2415,8 +2416,9 @@ void BytecodeGraphBuilder::VisitCallWithSpread() {
int const slot_id = bytecode_iterator().GetIndexOperand(3);
FeedbackSource feedback = CreateFeedbackSource(slot_id);
CallFrequency frequency = ComputeCallFrequency(slot_id);
SpeculationMode speculation_mode = GetSpeculationMode(slot_id);
const Operator* op = javascript()->CallWithSpread(
static_cast<int>(reg_count + 1), frequency, feedback);
static_cast<int>(reg_count + 1), frequency, feedback, speculation_mode);
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
op, args, static_cast<int>(arg_count), feedback.slot);

View File

@ -36,6 +36,24 @@ inline size_t hash_value(StackCheckKind kind) {
return static_cast<size_t>(kind);
}
// The CallFeedbackRelation states whether the target feedback stored with a
// JSCall is related to the call. If, during lowering, a JSCall (e.g. of a
// higher order function) is replaced by a JSCall with another target, the
// feedback has to be kept but is now unrelated.
enum class CallFeedbackRelation { kRelated, kUnrelated };
inline std::ostream& operator<<(std::ostream& os,
CallFeedbackRelation call_feedback_relation) {
switch (call_feedback_relation) {
case CallFeedbackRelation::kRelated:
return os << "CallFeedbackRelation::kRelated";
case CallFeedbackRelation::kUnrelated:
return os << "CallFeedbackRelation::kUnrelated";
}
UNREACHABLE();
return os;
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -314,8 +314,10 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
while (arity-- > 3) node->RemoveInput(3);
// Morph the {node} to a {JSCallWithArrayLike}.
NodeProperties::ChangeOp(node,
javascript()->CallWithArrayLike(p.frequency()));
NodeProperties::ChangeOp(
node, javascript()->CallWithArrayLike(
p.frequency(), p.feedback(), p.speculation_mode(),
CallFeedbackRelation::kUnrelated));
Reduction const reduction = ReduceJSCallWithArrayLike(node);
return reduction.Changed() ? reduction : Changed(node);
} else {
@ -342,8 +344,11 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
Node* effect0 = effect;
Node* control0 = control;
Node* value0 = effect0 = control0 = graph()->NewNode(
javascript()->CallWithArrayLike(p.frequency()), target, this_argument,
arguments_list, context, frame_state, effect0, control0);
javascript()->CallWithArrayLike(p.frequency(), p.feedback(),
p.speculation_mode(),
CallFeedbackRelation::kUnrelated),
target, this_argument, arguments_list, context, frame_state, effect0,
control0);
// Lower to {JSCall} if {arguments_list} is either null or undefined.
Node* effect1 = effect;
@ -387,14 +392,10 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
}
}
// Change {node} to the new {JSCall} operator.
// TODO(mslekova): Since this introduces a Call that will get optimized by
// the JSCallReducer, we basically might have to do all the serialization
// that we do for that here as well. The only difference is that here we
// disable speculation (cf. the empty FeedbackSource above), causing the
// JSCallReducer to do much less work. We should revisit this later.
NodeProperties::ChangeOp(
node,
javascript()->Call(arity, p.frequency(), FeedbackSource(), convert_mode));
node, javascript()->Call(arity, p.frequency(), p.feedback(), convert_mode,
p.speculation_mode(),
CallFeedbackRelation::kUnrelated));
// Try to further reduce the JSCall {node}.
Reduction const reduction = ReduceJSCall(node);
return reduction.Changed() ? reduction : Changed(node);
@ -569,8 +570,9 @@ Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
--arity;
}
NodeProperties::ChangeOp(
node,
javascript()->Call(arity, p.frequency(), FeedbackSource(), convert_mode));
node, javascript()->Call(arity, p.frequency(), p.feedback(), convert_mode,
p.speculation_mode(),
CallFeedbackRelation::kUnrelated));
// Try to further reduce the JSCall {node}.
Reduction const reduction = ReduceJSCall(node);
return reduction.Changed() ? reduction : Changed(node);
@ -801,8 +803,10 @@ Reduction JSCallReducer::ReduceReflectApply(Node* node) {
while (arity-- > 3) {
node->RemoveInput(arity);
}
NodeProperties::ChangeOp(node,
javascript()->CallWithArrayLike(p.frequency()));
NodeProperties::ChangeOp(
node, javascript()->CallWithArrayLike(p.frequency(), p.feedback(),
p.speculation_mode(),
CallFeedbackRelation::kUnrelated));
Reduction const reduction = ReduceJSCallWithArrayLike(node);
return reduction.Changed() ? reduction : Changed(node);
}
@ -1194,8 +1198,11 @@ Reduction JSCallReducer::ReduceArrayForEach(
outer_frame_state, ContinuationFrameStateMode::LAZY);
control = effect = graph()->NewNode(
javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
receiver, context, frame_state, effect, control);
javascript()->Call(5, p.frequency(), p.feedback(),
ConvertReceiverMode::kAny, p.speculation_mode(),
CallFeedbackRelation::kUnrelated),
fncallback, this_arg, element, k, receiver, context, frame_state, effect,
control);
// Rewire potential exception edges.
Node* on_exception = nullptr;
@ -1448,10 +1455,12 @@ Reduction JSCallReducer::ReduceArrayReduce(
&checkpoint_params[0], stack_parameters - 1, outer_frame_state,
ContinuationFrameStateMode::LAZY);
next_cur = control = effect =
graph()->NewNode(javascript()->Call(6, p.frequency()), fncallback,
jsgraph()->UndefinedConstant(), cur, element, k,
receiver, context, frame_state, effect, control);
next_cur = control = effect = graph()->NewNode(
javascript()->Call(6, p.frequency(), p.feedback(),
ConvertReceiverMode::kAny, p.speculation_mode(),
CallFeedbackRelation::kUnrelated),
fncallback, jsgraph()->UndefinedConstant(), cur, element, k, receiver,
context, frame_state, effect, control);
}
// Rewire potential exception edges.
@ -1643,8 +1652,11 @@ Reduction JSCallReducer::ReduceArrayMap(Node* node,
outer_frame_state, ContinuationFrameStateMode::LAZY);
Node* callback_value = control = effect = graph()->NewNode(
javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
receiver, context, frame_state, effect, control);
javascript()->Call(5, p.frequency(), p.feedback(),
ConvertReceiverMode::kAny, p.speculation_mode(),
CallFeedbackRelation::kUnrelated),
fncallback, this_arg, element, k, receiver, context, frame_state, effect,
control);
// Rewire potential exception edges.
Node* on_exception = nullptr;
@ -1864,8 +1876,11 @@ Reduction JSCallReducer::ReduceArrayFilter(
outer_frame_state, ContinuationFrameStateMode::LAZY);
callback_value = control = effect = graph()->NewNode(
javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
receiver, context, frame_state, effect, control);
javascript()->Call(5, p.frequency(), p.feedback(),
ConvertReceiverMode::kAny, p.speculation_mode(),
CallFeedbackRelation::kUnrelated),
fncallback, this_arg, element, k, receiver, context, frame_state,
effect, control);
}
// Rewire potential exception edges.
@ -2074,8 +2089,11 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant,
ContinuationFrameStateMode::LAZY);
callback_value = control = effect = graph()->NewNode(
javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
receiver, context, frame_state, effect, control);
javascript()->Call(5, p.frequency(), p.feedback(),
ConvertReceiverMode::kAny, p.speculation_mode(),
CallFeedbackRelation::kUnrelated),
fncallback, this_arg, element, k, receiver, context, frame_state,
effect, control);
}
// Rewire potential exception edges.
@ -2392,8 +2410,11 @@ Reduction JSCallReducer::ReduceArrayEvery(Node* node,
outer_frame_state, ContinuationFrameStateMode::LAZY);
callback_value = control = effect = graph()->NewNode(
javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
receiver, context, frame_state, effect, control);
javascript()->Call(5, p.frequency(), p.feedback(),
ConvertReceiverMode::kAny, p.speculation_mode(),
CallFeedbackRelation::kUnrelated),
fncallback, this_arg, element, k, receiver, context, frame_state,
effect, control);
}
// Rewire potential exception edges.
@ -2726,8 +2747,11 @@ Reduction JSCallReducer::ReduceArraySome(Node* node,
outer_frame_state, ContinuationFrameStateMode::LAZY);
callback_value = control = effect = graph()->NewNode(
javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
receiver, context, frame_state, effect, control);
javascript()->Call(5, p.frequency(), p.feedback(),
ConvertReceiverMode::kAny, p.speculation_mode(),
CallFeedbackRelation::kUnrelated),
fncallback, this_arg, element, k, receiver, context, frame_state,
effect, control);
}
// Rewire potential exception edges.
@ -3003,11 +3027,14 @@ bool IsSafeArgumentsElements(Node* node) {
Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
Node* node, int arity, CallFrequency const& frequency,
FeedbackSource const& feedback) {
FeedbackSource const& feedback, SpeculationMode speculation_mode,
CallFeedbackRelation feedback_relation) {
DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike ||
node->opcode() == IrOpcode::kJSCallWithSpread ||
node->opcode() == IrOpcode::kJSConstructWithArrayLike ||
node->opcode() == IrOpcode::kJSConstructWithSpread);
DCHECK_IMPLIES(speculation_mode == SpeculationMode::kAllowSpeculation,
feedback.IsValid());
Node* arguments_list = NodeProperties::GetValueInput(node, arity);
if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) {
@ -3145,7 +3172,9 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
if (node->opcode() == IrOpcode::kJSCallWithArrayLike ||
node->opcode() == IrOpcode::kJSCallWithSpread) {
NodeProperties::ChangeOp(
node, javascript()->Call(arity + 1, frequency, feedback));
node, javascript()->Call(arity + 1, frequency, feedback,
ConvertReceiverMode::kAny, speculation_mode,
CallFeedbackRelation::kUnrelated));
Reduction const reduction = ReduceJSCall(node);
return reduction.Changed() ? reduction : Changed(node);
} else {
@ -3286,8 +3315,9 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
}
NodeProperties::ChangeOp(
node, javascript()->Call(arity, p.frequency(), FeedbackSource(),
convert_mode));
node, javascript()->Call(arity, p.frequency(), p.feedback(),
convert_mode, p.speculation_mode(),
CallFeedbackRelation::kUnrelated));
// Try to further reduce the JSCall {node}.
Reduction const reduction = ReduceJSCall(node);
@ -3334,25 +3364,30 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
? ConvertReceiverMode::kAny
: ConvertReceiverMode::kNotNullOrUndefined;
NodeProperties::ChangeOp(
node, javascript()->Call(arity, p.frequency(), FeedbackSource(),
convert_mode));
node, javascript()->Call(arity, p.frequency(), p.feedback(),
convert_mode, p.speculation_mode(),
CallFeedbackRelation::kUnrelated));
// Try to further reduce the JSCall {node}.
Reduction const reduction = ReduceJSCall(node);
return reduction.Changed() ? reduction : Changed(node);
}
if (!p.feedback().IsValid()) return NoChange();
if (!ShouldUseCallICFeedback(target) ||
p.feedback_relation() != CallFeedbackRelation::kRelated ||
!p.feedback().IsValid()) {
return NoChange();
}
ProcessedFeedback const& feedback =
broker()->GetFeedbackForCall(FeedbackSource(p.feedback()));
broker()->GetFeedbackForCall(p.feedback());
if (feedback.IsInsufficient()) {
return ReduceSoftDeoptimize(
node, DeoptimizeReason::kInsufficientTypeFeedbackForCall);
}
base::Optional<HeapObjectRef> feedback_target = feedback.AsCall().target();
if (feedback_target.has_value() && ShouldUseCallICFeedback(target) &&
feedback_target->map().is_callable()) {
if (feedback_target.has_value() && feedback_target->map().is_callable()) {
Node* target_function = jsgraph()->Constant(*feedback_target);
// Check that the {target} is still the {target_function}.
@ -3728,9 +3763,12 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, node->opcode());
CallFrequency frequency = CallFrequencyOf(node->op());
return ReduceCallOrConstructWithArrayLikeOrSpread(node, 2, frequency,
FeedbackSource());
const CallParameters& p = CallParametersOf(node->op());
int arity = static_cast<int>(p.arity());
DCHECK_EQ(arity, 2);
return ReduceCallOrConstructWithArrayLikeOrSpread(
node, arity, p.frequency(), p.feedback(), p.speculation_mode(),
p.feedback_relation());
}
Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) {
@ -3740,8 +3778,9 @@ Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) {
int arity = static_cast<int>(p.arity() - 1);
CallFrequency frequency = p.frequency();
FeedbackSource feedback = p.feedback();
return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
feedback);
return ReduceCallOrConstructWithArrayLikeOrSpread(
node, arity, frequency, feedback, p.speculation_mode(),
p.feedback_relation());
}
Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
@ -4287,8 +4326,9 @@ Reduction JSCallReducer::ReduceStringPrototypeSubstr(Node* node) {
Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) {
DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode());
CallFrequency frequency = CallFrequencyOf(node->op());
return ReduceCallOrConstructWithArrayLikeOrSpread(node, 1, frequency,
FeedbackSource());
return ReduceCallOrConstructWithArrayLikeOrSpread(
node, 1, frequency, FeedbackSource(),
SpeculationMode::kDisallowSpeculation, CallFeedbackRelation::kRelated);
}
Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
@ -4298,8 +4338,9 @@ Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
int arity = static_cast<int>(p.arity() - 2);
CallFrequency frequency = p.frequency();
FeedbackSource feedback = p.feedback();
return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
feedback);
return ReduceCallOrConstructWithArrayLikeOrSpread(
node, arity, frequency, feedback, SpeculationMode::kDisallowSpeculation,
CallFeedbackRelation::kRelated);
}
Reduction JSCallReducer::ReduceReturnReceiver(Node* node) {
@ -5757,8 +5798,7 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) {
// 9. Call executor with both resolving functions
effect = control = graph()->NewNode(
javascript()->Call(4, p.frequency(), FeedbackSource(),
ConvertReceiverMode::kNullOrUndefined,
SpeculationMode::kDisallowSpeculation),
ConvertReceiverMode::kNullOrUndefined),
executor, jsgraph()->UndefinedConstant(), resolve, reject, context,
frame_state, effect, control);
@ -5770,8 +5810,7 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) {
// 10a. Call reject if the call to executor threw.
exception_effect = exception_control = graph()->NewNode(
javascript()->Call(3, p.frequency(), FeedbackSource(),
ConvertReceiverMode::kNullOrUndefined,
SpeculationMode::kDisallowSpeculation),
ConvertReceiverMode::kNullOrUndefined),
reject, jsgraph()->UndefinedConstant(), reason, context, frame_state,
exception_effect, exception_control);
@ -5862,7 +5901,8 @@ Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) {
NodeProperties::ChangeOp(
node, javascript()->Call(2 + arity, p.frequency(), p.feedback(),
ConvertReceiverMode::kNotNullOrUndefined,
p.speculation_mode()));
p.speculation_mode(),
CallFeedbackRelation::kUnrelated));
Reduction const reduction = ReducePromisePrototypeThen(node);
return reduction.Changed() ? reduction : Changed(node);
}
@ -5989,7 +6029,8 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) {
NodeProperties::ChangeOp(
node, javascript()->Call(2 + arity, p.frequency(), p.feedback(),
ConvertReceiverMode::kNotNullOrUndefined,
p.speculation_mode()));
p.speculation_mode(),
CallFeedbackRelation::kUnrelated));
Reduction const reduction = ReducePromisePrototypeThen(node);
return reduction.Changed() ? reduction : Changed(node);
}

View File

@ -7,6 +7,7 @@
#include "src/base/flags.h"
#include "src/compiler/frame-states.h"
#include "src/compiler/globals.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/node-properties.h"
#include "src/deoptimizer/deoptimize-reason.h"
@ -106,7 +107,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceCallOrConstructWithArrayLikeOrSpread(
Node* node, int arity, CallFrequency const& frequency,
FeedbackSource const& feedback);
FeedbackSource const& feedback, SpeculationMode speculation_mode,
CallFeedbackRelation feedback_relation);
Reduction ReduceJSConstruct(Node* node);
Reduction ReduceJSConstructWithArrayLike(Node* node);
Reduction ReduceJSConstructWithSpread(Node* node);

View File

@ -1439,7 +1439,8 @@ Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
: feedback.AsCall().speculation_mode();
const Operator* call_op =
javascript()->Call(2, CallFrequency(), p.callFeedback(),
ConvertReceiverMode::kNotNullOrUndefined, mode);
ConvertReceiverMode::kNotNullOrUndefined, mode,
CallFeedbackRelation::kRelated);
Node* call_property = graph()->NewNode(call_op, load_property, receiver,
context, frame_state, effect, control);
effect = call_property;

View File

@ -23,8 +23,7 @@ std::ostream& operator<<(std::ostream& os, CallFrequency const& f) {
}
CallFrequency CallFrequencyOf(Operator const* op) {
DCHECK(op->opcode() == IrOpcode::kJSCallWithArrayLike ||
op->opcode() == IrOpcode::kJSConstructWithArrayLike);
DCHECK_EQ(op->opcode(), IrOpcode::kJSConstructWithArrayLike);
return OpParameter<CallFrequency>(op);
}
@ -66,11 +65,13 @@ ConstructParameters const& ConstructParametersOf(Operator const* op) {
}
std::ostream& operator<<(std::ostream& os, CallParameters const& p) {
return os << p.arity() << ", " << p.frequency() << ", " << p.convert_mode();
return os << p.arity() << ", " << p.frequency() << ", " << p.convert_mode()
<< ", " << p.speculation_mode() << ", " << p.feedback_relation();
}
const CallParameters& CallParametersOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kJSCall ||
op->opcode() == IrOpcode::kJSCallWithArrayLike ||
op->opcode() == IrOpcode::kJSCallWithSpread);
return OpParameter<CallParameters>(op);
}
@ -882,15 +883,12 @@ const Operator* JSOperatorBuilder::CallForwardVarargs(size_t arity,
parameters); // parameter
}
const Operator* JSOperatorBuilder::Call(size_t arity,
CallFrequency const& frequency,
FeedbackSource const& feedback,
ConvertReceiverMode convert_mode,
SpeculationMode speculation_mode) {
DCHECK_IMPLIES(speculation_mode == SpeculationMode::kAllowSpeculation,
feedback.IsValid());
const Operator* JSOperatorBuilder::Call(
size_t arity, CallFrequency const& frequency,
FeedbackSource const& feedback, ConvertReceiverMode convert_mode,
SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) {
CallParameters parameters(arity, frequency, feedback, convert_mode,
speculation_mode);
speculation_mode, feedback_relation);
return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCall, Operator::kNoProperties, // opcode
"JSCall", // name
@ -899,21 +897,26 @@ const Operator* JSOperatorBuilder::Call(size_t arity,
}
const Operator* JSOperatorBuilder::CallWithArrayLike(
CallFrequency const& frequency) {
return new (zone()) Operator1<CallFrequency>( // --
const CallFrequency& frequency, const FeedbackSource& feedback,
SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) {
CallParameters parameters(2, frequency, feedback, ConvertReceiverMode::kAny,
speculation_mode, feedback_relation);
return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCallWithArrayLike, Operator::kNoProperties, // opcode
"JSCallWithArrayLike", // name
3, 1, 1, 1, 1, 2, // counts
frequency); // parameter
parameters); // parameter
}
const Operator* JSOperatorBuilder::CallWithSpread(
uint32_t arity, CallFrequency const& frequency,
FeedbackSource const& feedback, SpeculationMode speculation_mode) {
FeedbackSource const& feedback, SpeculationMode speculation_mode,
CallFeedbackRelation feedback_relation) {
DCHECK_IMPLIES(speculation_mode == SpeculationMode::kAllowSpeculation,
feedback.IsValid());
CallParameters parameters(arity, frequency, feedback,
ConvertReceiverMode::kAny, speculation_mode);
ConvertReceiverMode::kAny, speculation_mode,
feedback_relation);
return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCallWithSpread, Operator::kNoProperties, // opcode
"JSCallWithSpread", // name

View File

@ -165,12 +165,20 @@ class CallParameters final {
CallParameters(size_t arity, CallFrequency const& frequency,
FeedbackSource const& feedback,
ConvertReceiverMode convert_mode,
SpeculationMode speculation_mode)
SpeculationMode speculation_mode,
CallFeedbackRelation feedback_relation)
: bit_field_(ArityField::encode(arity) |
CallFeedbackRelationField::encode(feedback_relation) |
SpeculationModeField::encode(speculation_mode) |
ConvertReceiverModeField::encode(convert_mode)),
frequency_(frequency),
feedback_(feedback) {}
feedback_(feedback) {
// CallFeedbackRelation is ignored if the feedback slot is invalid.
DCHECK_IMPLIES(speculation_mode == SpeculationMode::kAllowSpeculation,
feedback.IsValid());
DCHECK_IMPLIES(!feedback.IsValid(),
feedback_relation == CallFeedbackRelation::kUnrelated);
}
size_t arity() const { return ArityField::decode(bit_field_); }
CallFrequency const& frequency() const { return frequency_; }
@ -183,6 +191,10 @@ class CallParameters final {
return SpeculationModeField::decode(bit_field_);
}
CallFeedbackRelation feedback_relation() const {
return CallFeedbackRelationField::decode(bit_field_);
}
bool operator==(CallParameters const& that) const {
return this->bit_field_ == that.bit_field_ &&
this->frequency_ == that.frequency_ &&
@ -197,7 +209,8 @@ class CallParameters final {
feedback_hash(p.feedback_));
}
using ArityField = BitField<size_t, 0, 28>;
using ArityField = BitField<size_t, 0, 27>;
using CallFeedbackRelationField = BitField<CallFeedbackRelation, 27, 1>;
using SpeculationModeField = BitField<SpeculationMode, 28, 1>;
using ConvertReceiverModeField = BitField<ConvertReceiverMode, 29, 2>;
@ -815,12 +828,19 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
size_t arity, CallFrequency const& frequency = CallFrequency(),
FeedbackSource const& feedback = FeedbackSource(),
ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny,
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation);
const Operator* CallWithArrayLike(CallFrequency const& frequency);
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation,
CallFeedbackRelation feedback_relation =
CallFeedbackRelation::kUnrelated);
const Operator* CallWithArrayLike(
CallFrequency const& frequency,
const FeedbackSource& feedback = FeedbackSource{},
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation,
CallFeedbackRelation feedback_relation = CallFeedbackRelation::kRelated);
const Operator* CallWithSpread(
uint32_t arity, CallFrequency const& frequency = CallFrequency(),
FeedbackSource const& feedback = FeedbackSource(),
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation);
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation,
CallFeedbackRelation feedback_relation = CallFeedbackRelation::kRelated);
const Operator* CallRuntime(Runtime::FunctionId id);
const Operator* CallRuntime(Runtime::FunctionId id, size_t arity);
const Operator* CallRuntime(const Runtime::Function* function, size_t arity);

View File

@ -1804,8 +1804,9 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
// Maybe we did at least learn something about the {receiver}.
if (p.convert_mode() != convert_mode) {
NodeProperties::ChangeOp(
node, javascript()->Call(p.arity(), p.frequency(), p.feedback(),
convert_mode, p.speculation_mode()));
node,
javascript()->Call(p.arity(), p.frequency(), p.feedback(), convert_mode,
p.speculation_mode(), p.feedback_relation()));
return Changed(node);
}

View File

@ -2138,14 +2138,12 @@ void SerializerForBackgroundCompilation::ProcessBuiltinCall(
new_arguments.push_back(arguments[0]); // O
for (auto constant : callback.constants()) {
ProcessCalleeForCallOrConstruct(constant, base::nullopt,
new_arguments,
SpeculationMode::kDisallowSpeculation,
new_arguments, speculation_mode,
kMissingArgumentsAreUndefined);
}
for (auto blueprint : callback.function_blueprints()) {
ProcessCalleeForCallOrConstruct(Callee(blueprint), base::nullopt,
new_arguments,
SpeculationMode::kDisallowSpeculation,
new_arguments, speculation_mode,
kMissingArgumentsAreUndefined);
}
}
@ -2165,14 +2163,12 @@ void SerializerForBackgroundCompilation::ProcessBuiltinCall(
new_arguments.push_back(arguments[0]); // O
for (auto constant : callback.constants()) {
ProcessCalleeForCallOrConstruct(constant, base::nullopt,
new_arguments,
SpeculationMode::kDisallowSpeculation,
new_arguments, speculation_mode,
kMissingArgumentsAreUndefined);
}
for (auto blueprint : callback.function_blueprints()) {
ProcessCalleeForCallOrConstruct(Callee(blueprint), base::nullopt,
new_arguments,
SpeculationMode::kDisallowSpeculation,
new_arguments, speculation_mode,
kMissingArgumentsAreUndefined);
}
}
@ -2189,8 +2185,7 @@ void SerializerForBackgroundCompilation::ProcessBuiltinCall(
HintsVector new_arguments({new_receiver}, zone());
for (auto constant : arguments[0].constants()) {
ProcessCalleeForCallOrConstruct(constant, base::nullopt,
new_arguments,
SpeculationMode::kDisallowSpeculation,
new_arguments, speculation_mode,
kMissingArgumentsAreUnknown);
}
}
@ -2222,9 +2217,9 @@ void SerializerForBackgroundCompilation::ProcessBuiltinCall(
HintsVector new_arguments(arguments.begin() + 1, arguments.end(),
zone());
for (auto constant : arguments[0].constants()) {
ProcessCalleeForCallOrConstruct(
constant, base::nullopt, new_arguments,
SpeculationMode::kDisallowSpeculation, padding);
ProcessCalleeForCallOrConstruct(constant, base::nullopt,
new_arguments, speculation_mode,
padding);
}
}
break;

View File

@ -0,0 +1,250 @@
// 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.
// Flags: --allow-natives-syntax --opt --no-always-opt
"use strict";
const mathAbs = Math.abs;
const mathImul = Math.imul;
// Testing: FunctionPrototypeApply
function TestFunctionPrototypeApplyHelper() {
return mathAbs.apply(undefined, arguments);
}
function TestFunctionPrototypeApply(x) {
return TestFunctionPrototypeApplyHelper(x);
}
%PrepareFunctionForOptimization(TestFunctionPrototypeApplyHelper);
%PrepareFunctionForOptimization(TestFunctionPrototypeApply);
assertEquals(TestFunctionPrototypeApply(-13), 13);
assertEquals(TestFunctionPrototypeApply(42), 42);
%OptimizeFunctionOnNextCall(TestFunctionPrototypeApply);
assertEquals(TestFunctionPrototypeApply(-13), 13);
assertOptimized(TestFunctionPrototypeApply);
TestFunctionPrototypeApply("abc");
assertUnoptimized(TestFunctionPrototypeApply);
// Testing: FunctionPrototypeCall
function TestFunctionPrototypeCall(x) {
return mathAbs.call(undefined, x);
}
%PrepareFunctionForOptimization(TestFunctionPrototypeCall);
TestFunctionPrototypeCall(42);
TestFunctionPrototypeCall(52);
%OptimizeFunctionOnNextCall(TestFunctionPrototypeCall);
TestFunctionPrototypeCall(12);
assertOptimized(TestFunctionPrototypeCall);
TestFunctionPrototypeCall("abc");
assertUnoptimized(TestFunctionPrototypeCall);
// Testing: ArrayForEach
function TestArrayForEach(x) {
x.forEach(mathAbs);
}
%PrepareFunctionForOptimization(TestArrayForEach);
TestArrayForEach([1, 3, -4]);
TestArrayForEach([-9, 9, 0]);
%OptimizeFunctionOnNextCall(TestArrayForEach);
TestArrayForEach([1, 3, -4]);
assertOptimized(TestArrayForEach);
TestArrayForEach(["abc", "xy"]);
assertUnoptimized(TestArrayForEach);
// Testing: ArrayReduce
function TestArrayReduce(x) {
return x.reduce(mathImul);
}
%PrepareFunctionForOptimization(TestArrayReduce);
assertEquals(TestArrayReduce([1, 2, -3, 4]), -24);
assertEquals(TestArrayReduce([3, 5, 7]), 105);
%OptimizeFunctionOnNextCall(TestArrayReduce);
assertEquals(TestArrayReduce([1, 2, -3, 4]), -24);
assertOptimized(TestArrayReduce);
TestArrayReduce(["abc", "xy"]);
assertUnoptimized(TestArrayReduce);
// Testing: ArrayReduceRight
function TestArrayReduceRight(x) {
return x.reduceRight(mathImul);
}
%PrepareFunctionForOptimization(TestArrayReduceRight);
assertEquals(TestArrayReduceRight([1, 2, -3, 4]), -24);
assertEquals(TestArrayReduceRight([3, 5, 7]), 105);
%OptimizeFunctionOnNextCall(TestArrayReduceRight);
assertEquals(TestArrayReduceRight([1, 2, -3, 4]), -24);
assertOptimized(TestArrayReduceRight);
TestArrayReduceRight(["abc", "xy"]);
assertUnoptimized(TestArrayReduceRight);
// Testing: ArrayMap
function TestArrayMap(x) {
return x.map(mathAbs);
}
%PrepareFunctionForOptimization(TestArrayMap);
assertEquals(TestArrayMap([1, -2, -3, 4]), [1, 2, 3, 4]);
assertEquals(TestArrayMap([5, -5, 5, -5]), [5, 5, 5, 5]);
%OptimizeFunctionOnNextCall(TestArrayMap);
assertEquals(TestArrayMap([1, -2, 3, -4]), [1, 2, 3, 4]);
assertOptimized(TestArrayMap);
TestArrayMap(["abc", "xy"]);
assertUnoptimized(TestArrayMap);
// Testing: ArrayFilter
function TestArrayFilter(x) {
return x.filter(mathAbs);
}
%PrepareFunctionForOptimization(TestArrayFilter);
assertEquals(TestArrayFilter([-2, 0, 3, -4]), [-2, 3, -4]);
assertEquals(TestArrayFilter([0, 1, 1, 0]), [1, 1]);
%OptimizeFunctionOnNextCall(TestArrayFilter);
assertEquals(TestArrayFilter([-2, 0, 3, -4]), [-2, 3, -4]);
assertOptimized(TestArrayFilter);
TestArrayFilter(["abc", "xy"]);
assertUnoptimized(TestArrayFilter);
// Testing: ArrayFind
function TestArrayFind(x) {
return x.find(mathAbs);
}
%PrepareFunctionForOptimization(TestArrayFind);
assertEquals(TestArrayFind([0, 0, -3, 12]), -3);
assertEquals(TestArrayFind([0, -18]), -18);
%OptimizeFunctionOnNextCall(TestArrayFind);
assertEquals(TestArrayFind([0, 0, -3, 12]), -3);
assertOptimized(TestArrayFind);
TestArrayFind(["", "abc", "xy"]);
assertUnoptimized(TestArrayFind);
// Testing: ArrayFindIndex
function TestArrayFindIndex(x) {
return x.findIndex(mathAbs);
}
%PrepareFunctionForOptimization(TestArrayFindIndex);
assertEquals(TestArrayFindIndex([0, 0, -3, 12]), 2);
assertEquals(TestArrayFindIndex([0, -18]), 1);
%OptimizeFunctionOnNextCall(TestArrayFindIndex);
assertEquals(TestArrayFindIndex([0, 0, -3, 12]), 2);
assertOptimized(TestArrayFindIndex);
TestArrayFindIndex(["", "abc", "xy"]);
assertUnoptimized(TestArrayFindIndex);
// Testing: ArrayEvery
function TestArrayEvery(x) {
return x.every(mathAbs);
}
%PrepareFunctionForOptimization(TestArrayEvery);
assertEquals(TestArrayEvery([3, 0, -9]), false);
assertEquals(TestArrayEvery([2, 12, -1]), true);
%OptimizeFunctionOnNextCall(TestArrayEvery);
assertEquals(TestArrayEvery([3, 0, -9]), false);
assertOptimized(TestArrayEvery);
TestArrayEvery(["abc", "xy"]);
assertUnoptimized(TestArrayEvery);
// Testing: ArraySome
function TestArraySome(x) {
return x.some(mathAbs);
}
%PrepareFunctionForOptimization(TestArraySome);
assertEquals(TestArraySome([3, 0, -9]), true);
assertEquals(TestArraySome([0, 0]), false);
%OptimizeFunctionOnNextCall(TestArraySome);
assertEquals(TestArraySome([3, 0, -9]), true);
assertOptimized(TestArraySome);
TestArraySome(["abc", "xy"]);
assertUnoptimized(TestArraySome);
// Testing: JSCall (JSFunction)
const boundMathImul = mathImul.bind(undefined, -3);
function TestJSCallWithJSFunction(x) {
return boundMathImul(x);
}
%PrepareFunctionForOptimization(TestJSCallWithJSFunction);
assertEquals(TestJSCallWithJSFunction(-14), 42);
assertEquals(TestJSCallWithJSFunction(14), -42);
%OptimizeFunctionOnNextCall(TestJSCallWithJSFunction);
assertEquals(TestJSCallWithJSFunction(-14), 42);
assertOptimized(TestJSCallWithJSFunction);
TestJSCallWithJSFunction("abc");
assertUnoptimized(TestJSCallWithJSFunction);
// Testing: JSCall (JSBoundFunction)
function TestJSCallWithJSBoundFunction(x) {
return mathImul.bind(undefined, -3)(x);
}
%PrepareFunctionForOptimization(TestJSCallWithJSBoundFunction);
assertEquals(TestJSCallWithJSBoundFunction(-14), 42);
assertEquals(TestJSCallWithJSBoundFunction(14), -42);
%OptimizeFunctionOnNextCall(TestJSCallWithJSBoundFunction);
assertEquals(TestJSCallWithJSBoundFunction(-14), 42);
assertOptimized(TestJSCallWithJSBoundFunction);
TestJSCallWithJSBoundFunction("abc");
assertUnoptimized(TestJSCallWithJSBoundFunction);
// Testing: ReflectApply
function TestReflectApplyHelper() {
return Reflect.apply(mathAbs, undefined, arguments);
}
function TestReflectApply(x) {
return TestReflectApplyHelper(x);
}
%PrepareFunctionForOptimization(TestReflectApplyHelper);
%PrepareFunctionForOptimization(TestReflectApply);
assertEquals(TestReflectApply(-9), 9);
assertEquals(TestReflectApply(7), 7);
%OptimizeFunctionOnNextCall(TestReflectApply);
assertEquals(TestReflectApply(-9), 9);
assertOptimized(TestReflectApply);
TestReflectApply("abc");
assertUnoptimized(TestReflectApply);
// Testing: CallWithSpread
function TestCallWithSpreadHelper() {
return mathImul(...arguments);
}
function TestCallWithSpread(x) {
return TestCallWithSpreadHelper(x, x);
}
%PrepareFunctionForOptimization(TestCallWithSpreadHelper);
%PrepareFunctionForOptimization(TestCallWithSpread);
assertEquals(TestCallWithSpread(-13), 169);
assertEquals(TestCallWithSpread(7), 49);
%OptimizeFunctionOnNextCall(TestCallWithSpread);
assertEquals(TestCallWithSpread(-13), 169);
assertOptimized(TestCallWithSpread);
TestCallWithSpread("abc");
assertUnoptimized(TestCallWithSpread);

View File

@ -1106,6 +1106,9 @@
'compiler/diamond-followedby-branch': [SKIP],
'compiler/load-elimination-const-field': [SKIP],
'compiler/constant-fold-add-static': [SKIP],
# Some tests rely on inlining.
'compiler/opt-higher-order-functions': [SKIP],
}], # variant == turboprop
##############################################################################

View File

@ -115,9 +115,9 @@ class JSCallReducerTest : public TypedGraphTest {
Handle<FeedbackVector> vector =
FeedbackVector::New(isolate(), shared, closure_feedback_cell_array);
FeedbackSource feedback(vector, FeedbackSlot(0));
return javascript()->Call(arity, CallFrequency(), feedback,
ConvertReceiverMode::kAny,
SpeculationMode::kAllowSpeculation);
return javascript()->Call(
arity, CallFrequency(), feedback, ConvertReceiverMode::kAny,
SpeculationMode::kAllowSpeculation, CallFeedbackRelation::kRelated);
}
private: