Commit 5ee1b7ad authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Add IC support for Call/ConstructWithSpread.

Properly hook up the (existing) IC slots for the CallWithSpread and
ConstructWithSpread bytecodes, and change the interpreter to collect
feedback (call counts and regular target function feedback) for those.
There's no integration with the Array constructor yet, since that
requires some yak shaving to thread through the AllocationSite to the
Array constructor stub. Once we have a solution for that, we can also
remove the current code duplication in the Call/Construct IC logic.

Also properly hook up the newly available feedback in TurboFan. This
will fix not only the missing target feedback, but more importantly
the tear-up decisions for optimization are correct now in the presence
of spread calls, and even more importantly the inlining heurstic has
proper call frequencies for those.

Some follow-up changes will be necessary to make sure we use the
feedback even for corner cases that aren't handled properly yet. Also
we should consider collecting feedback about the map of the spread
at some point to be able to always inline the spread calls.

Bug: v8:6399, v8:6527, v8:6630
Change-Id: I818dbcb411fd3951d8e9d31f5d7e794f8d60fa00
Reviewed-on: https://chromium-review.googlesource.com/582647Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46832}
parent e0e3049e
...@@ -1504,12 +1504,30 @@ void BytecodeGraphBuilder::VisitCallWithSpread() { ...@@ -1504,12 +1504,30 @@ void BytecodeGraphBuilder::VisitCallWithSpread() {
Node* callee = Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0)); environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1); interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
Node* receiver_node = environment()->LookupRegister(receiver);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2); size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
const Operator* call = interpreter::Register first_arg = interpreter::Register(receiver.index() + 1);
javascript()->CallWithSpread(static_cast<int>(reg_count + 1)); int arg_count = static_cast<int>(reg_count) - 1;
Node* const* args =
GetCallArgumentsFromRegister(callee, receiver_node, first_arg, arg_count);
// Slot index of 0 is used indicate no feedback slot is available. Assert
// the assumption that slot index 0 is never a valid feedback slot.
int const slot_id = bytecode_iterator().GetIndexOperand(3);
STATIC_ASSERT(FeedbackVector::kReservedIndexCount > 0);
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
Node* value = ProcessCallArguments(call, callee, receiver, reg_count); CallFrequency frequency = ComputeCallFrequency(slot_id);
environment()->BindAccumulator(value, Environment::kAttachFrameState); const Operator* op = javascript()->CallWithSpread(
static_cast<int>(reg_count + 1), frequency, feedback);
Node* node = nullptr;
if (Node* simplified = TryBuildSimplifiedCall(
op, args, static_cast<int>(arg_count), feedback.slot())) {
if (environment() == nullptr) return;
node = simplified;
} else {
node = ProcessCallArguments(op, args, 2 + arg_count);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
} }
void BytecodeGraphBuilder::VisitCallJSRuntime() { void BytecodeGraphBuilder::VisitCallJSRuntime() {
...@@ -1626,17 +1644,30 @@ void BytecodeGraphBuilder::VisitConstructWithSpread() { ...@@ -1626,17 +1644,30 @@ void BytecodeGraphBuilder::VisitConstructWithSpread() {
interpreter::Register callee_reg = bytecode_iterator().GetRegisterOperand(0); interpreter::Register callee_reg = bytecode_iterator().GetRegisterOperand(0);
interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1); interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2); size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
// Slot index of 0 is used indicate no feedback slot is available. Assert
// the assumption that slot index 0 is never a valid feedback slot.
STATIC_ASSERT(FeedbackVector::kReservedIndexCount > 0);
int const slot_id = bytecode_iterator().GetIndexOperand(3);
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
Node* new_target = environment()->LookupAccumulator(); Node* new_target = environment()->LookupAccumulator();
Node* callee = environment()->LookupRegister(callee_reg); Node* callee = environment()->LookupRegister(callee_reg);
const Operator* op = CallFrequency frequency = ComputeCallFrequency(slot_id);
javascript()->ConstructWithSpread(static_cast<uint32_t>(reg_count + 2)); const Operator* op = javascript()->ConstructWithSpread(
static_cast<uint32_t>(reg_count + 2), frequency, feedback);
int arg_count = static_cast<int>(reg_count); int arg_count = static_cast<int>(reg_count);
Node* const* args = GetConstructArgumentsFromRegister(callee, new_target, Node* const* args = GetConstructArgumentsFromRegister(callee, new_target,
first_reg, arg_count); first_reg, arg_count);
Node* value = ProcessConstructArguments(op, args, 2 + arg_count); Node* node = nullptr;
environment()->BindAccumulator(value, Environment::kAttachFrameState); if (Node* simplified = TryBuildSimplifiedConstruct(
op, args, static_cast<int>(arg_count), feedback.slot())) {
if (environment() == nullptr) return;
node = simplified;
} else {
node = ProcessConstructArguments(op, args, 2 + arg_count);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
} }
void BytecodeGraphBuilder::VisitInvokeIntrinsic() { void BytecodeGraphBuilder::VisitInvokeIntrinsic() {
......
...@@ -934,7 +934,8 @@ bool IsSafeArgumentsElements(Node* node) { ...@@ -934,7 +934,8 @@ bool IsSafeArgumentsElements(Node* node) {
} // namespace } // namespace
Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
Node* node, int arity, CallFrequency const& frequency) { Node* node, int arity, CallFrequency const& frequency,
VectorSlotPair const& feedback) {
DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike || DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike ||
node->opcode() == IrOpcode::kJSCallWithSpread || node->opcode() == IrOpcode::kJSCallWithSpread ||
node->opcode() == IrOpcode::kJSConstructWithArrayLike || node->opcode() == IrOpcode::kJSConstructWithArrayLike ||
...@@ -989,14 +990,14 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( ...@@ -989,14 +990,14 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
break; break;
case IrOpcode::kJSCallWithSpread: { case IrOpcode::kJSCallWithSpread: {
// Ignore uses as spread input to calls with spread. // Ignore uses as spread input to calls with spread.
SpreadWithArityParameter p = SpreadWithArityParameterOf(user->op()); CallParameters p = CallParametersOf(user->op());
int const arity = static_cast<int>(p.arity() - 1); int const arity = static_cast<int>(p.arity() - 1);
if (user->InputAt(arity) == arguments_list) continue; if (user->InputAt(arity) == arguments_list) continue;
break; break;
} }
case IrOpcode::kJSConstructWithSpread: { case IrOpcode::kJSConstructWithSpread: {
// Ignore uses as spread input to construct with spread. // Ignore uses as spread input to construct with spread.
SpreadWithArityParameter p = SpreadWithArityParameterOf(user->op()); ConstructParameters p = ConstructParametersOf(user->op());
int const arity = static_cast<int>(p.arity() - 2); int const arity = static_cast<int>(p.arity() - 2);
if (user->InputAt(arity) == arguments_list) continue; if (user->InputAt(arity) == arguments_list) continue;
break; break;
...@@ -1089,12 +1090,13 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( ...@@ -1089,12 +1090,13 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
if (node->opcode() == IrOpcode::kJSCallWithArrayLike || if (node->opcode() == IrOpcode::kJSCallWithArrayLike ||
node->opcode() == IrOpcode::kJSCallWithSpread) { node->opcode() == IrOpcode::kJSCallWithSpread) {
NodeProperties::ChangeOp(node, javascript()->Call(arity + 1, frequency)); NodeProperties::ChangeOp(
node, javascript()->Call(arity + 1, frequency, feedback));
Reduction const reduction = ReduceJSCall(node); Reduction const reduction = ReduceJSCall(node);
return reduction.Changed() ? reduction : Changed(node); return reduction.Changed() ? reduction : Changed(node);
} else { } else {
NodeProperties::ChangeOp(node, NodeProperties::ChangeOp(
javascript()->Construct(arity + 2, frequency)); node, javascript()->Construct(arity + 2, frequency, feedback));
Reduction const reduction = ReduceJSConstruct(node); Reduction const reduction = ReduceJSConstruct(node);
return reduction.Changed() ? reduction : Changed(node); return reduction.Changed() ? reduction : Changed(node);
} }
...@@ -1293,19 +1295,20 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { ...@@ -1293,19 +1295,20 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node) { Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, node->opcode()); DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, node->opcode());
CallFrequency frequency = CallFrequencyOf(node->op()); CallFrequency frequency = CallFrequencyOf(node->op());
return ReduceCallOrConstructWithArrayLikeOrSpread(node, 2, frequency); VectorSlotPair feedback;
return ReduceCallOrConstructWithArrayLikeOrSpread(node, 2, frequency,
feedback);
} }
Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) { Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode()); DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode());
SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); CallParameters const& p = CallParametersOf(node->op());
DCHECK_LE(3u, p.arity()); DCHECK_LE(3u, p.arity());
int arity = static_cast<int>(p.arity() - 1); int arity = static_cast<int>(p.arity() - 1);
CallFrequency frequency = p.frequency();
// TODO(turbofan): Collect call counts on spread call/construct and thread it VectorSlotPair feedback = p.feedback();
// through here. return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
CallFrequency frequency; feedback);
return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency);
} }
Reduction JSCallReducer::ReduceJSConstruct(Node* node) { Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
...@@ -1436,19 +1439,20 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { ...@@ -1436,19 +1439,20 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) { Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) {
DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode()); DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode());
CallFrequency frequency = CallFrequencyOf(node->op()); CallFrequency frequency = CallFrequencyOf(node->op());
return ReduceCallOrConstructWithArrayLikeOrSpread(node, 1, frequency); VectorSlotPair feedback;
return ReduceCallOrConstructWithArrayLikeOrSpread(node, 1, frequency,
feedback);
} }
Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode()); DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode());
SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); ConstructParameters const& p = ConstructParametersOf(node->op());
DCHECK_LE(3u, p.arity()); DCHECK_LE(3u, p.arity());
int arity = static_cast<int>(p.arity() - 2); int arity = static_cast<int>(p.arity() - 2);
CallFrequency frequency = p.frequency();
// TODO(turbofan): Collect call counts on spread call/construct and thread it VectorSlotPair feedback = p.feedback();
// through here. return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
CallFrequency frequency; feedback);
return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency);
} }
Reduction JSCallReducer::ReduceReturnReceiver(Node* node) { Reduction JSCallReducer::ReduceReturnReceiver(Node* node) {
......
...@@ -24,6 +24,7 @@ class CommonOperatorBuilder; ...@@ -24,6 +24,7 @@ class CommonOperatorBuilder;
class JSGraph; class JSGraph;
class JSOperatorBuilder; class JSOperatorBuilder;
class SimplifiedOperatorBuilder; class SimplifiedOperatorBuilder;
class VectorSlotPair;
// Performs strength reduction on {JSConstruct} and {JSCall} nodes, // Performs strength reduction on {JSConstruct} and {JSCall} nodes,
// which might allow inlining or other optimizations to be performed afterwards. // which might allow inlining or other optimizations to be performed afterwards.
...@@ -69,7 +70,8 @@ class JSCallReducer final : public AdvancedReducer { ...@@ -69,7 +70,8 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node); Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node);
Reduction ReduceArrayMap(Handle<JSFunction> function, Node* node); Reduction ReduceArrayMap(Handle<JSFunction> function, Node* node);
Reduction ReduceCallOrConstructWithArrayLikeOrSpread( Reduction ReduceCallOrConstructWithArrayLikeOrSpread(
Node* node, int arity, CallFrequency const& frequency); Node* node, int arity, CallFrequency const& frequency,
VectorSlotPair const& feedback);
Reduction ReduceJSConstruct(Node* node); Reduction ReduceJSConstruct(Node* node);
Reduction ReduceJSConstructWithArrayLike(Node* node); Reduction ReduceJSConstructWithArrayLike(Node* node);
Reduction ReduceJSConstructWithSpread(Node* node); Reduction ReduceJSConstructWithSpread(Node* node);
......
...@@ -593,7 +593,7 @@ void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) { ...@@ -593,7 +593,7 @@ void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) {
} }
void JSGenericLowering::LowerJSConstructWithSpread(Node* node) { void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); ConstructParameters const& p = ConstructParametersOf(node->op());
int const arg_count = static_cast<int>(p.arity() - 2); int const arg_count = static_cast<int>(p.arity() - 2);
int const spread_index = arg_count; int const spread_index = arg_count;
int const new_target_index = arg_count + 1; int const new_target_index = arg_count + 1;
...@@ -664,7 +664,7 @@ void JSGenericLowering::LowerJSCallWithArrayLike(Node* node) { ...@@ -664,7 +664,7 @@ void JSGenericLowering::LowerJSCallWithArrayLike(Node* node) {
} }
void JSGenericLowering::LowerJSCallWithSpread(Node* node) { void JSGenericLowering::LowerJSCallWithSpread(Node* node) {
SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); CallParameters const& p = CallParametersOf(node->op());
int const arg_count = static_cast<int>(p.arity() - 2); int const arg_count = static_cast<int>(p.arity() - 2);
int const spread_index = static_cast<int>(p.arity() + 1); int const spread_index = static_cast<int>(p.arity() + 1);
CallDescriptor::Flags flags = FrameStateFlagForCall(node); CallDescriptor::Flags flags = FrameStateFlagForCall(node);
......
...@@ -94,40 +94,18 @@ std::ostream& operator<<(std::ostream& os, ConstructParameters const& p) { ...@@ -94,40 +94,18 @@ std::ostream& operator<<(std::ostream& os, ConstructParameters const& p) {
} }
ConstructParameters const& ConstructParametersOf(Operator const* op) { ConstructParameters const& ConstructParametersOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kJSConstruct, op->opcode()); DCHECK(op->opcode() == IrOpcode::kJSConstruct ||
op->opcode() == IrOpcode::kJSConstructWithSpread);
return OpParameter<ConstructParameters>(op); return OpParameter<ConstructParameters>(op);
} }
bool operator==(SpreadWithArityParameter const& lhs,
SpreadWithArityParameter const& rhs) {
return lhs.arity() == rhs.arity();
}
bool operator!=(SpreadWithArityParameter const& lhs,
SpreadWithArityParameter const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(SpreadWithArityParameter const& p) {
return base::hash_combine(p.arity());
}
std::ostream& operator<<(std::ostream& os, SpreadWithArityParameter const& p) {
return os << p.arity();
}
SpreadWithArityParameter const& SpreadWithArityParameterOf(Operator const* op) {
DCHECK(op->opcode() == IrOpcode::kJSConstructWithSpread ||
op->opcode() == IrOpcode::kJSCallWithSpread);
return OpParameter<SpreadWithArityParameter>(op);
}
std::ostream& operator<<(std::ostream& os, CallParameters const& p) { 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();
} }
const CallParameters& CallParametersOf(const Operator* op) { const CallParameters& CallParametersOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kJSCall, op->opcode()); DCHECK(op->opcode() == IrOpcode::kJSCall ||
op->opcode() == IrOpcode::kJSCallWithSpread);
return OpParameter<CallParameters>(op); return OpParameter<CallParameters>(op);
} }
...@@ -768,9 +746,11 @@ const Operator* JSOperatorBuilder::CallWithArrayLike(CallFrequency frequency) { ...@@ -768,9 +746,11 @@ const Operator* JSOperatorBuilder::CallWithArrayLike(CallFrequency frequency) {
frequency); // parameter frequency); // parameter
} }
const Operator* JSOperatorBuilder::CallWithSpread(uint32_t arity) { const Operator* JSOperatorBuilder::CallWithSpread(
SpreadWithArityParameter parameters(arity); uint32_t arity, CallFrequency frequency, VectorSlotPair const& feedback) {
return new (zone()) Operator1<SpreadWithArityParameter>( // -- CallParameters parameters(arity, frequency, feedback,
ConvertReceiverMode::kAny);
return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCallWithSpread, Operator::kNoProperties, // opcode IrOpcode::kJSCallWithSpread, Operator::kNoProperties, // opcode
"JSCallWithSpread", // name "JSCallWithSpread", // name
parameters.arity(), 1, 1, 1, 1, 2, // counts parameters.arity(), 1, 1, 1, 1, 2, // counts
...@@ -832,9 +812,10 @@ const Operator* JSOperatorBuilder::ConstructWithArrayLike( ...@@ -832,9 +812,10 @@ const Operator* JSOperatorBuilder::ConstructWithArrayLike(
frequency); // parameter frequency); // parameter
} }
const Operator* JSOperatorBuilder::ConstructWithSpread(uint32_t arity) { const Operator* JSOperatorBuilder::ConstructWithSpread(
SpreadWithArityParameter parameters(arity); uint32_t arity, CallFrequency frequency, VectorSlotPair const& feedback) {
return new (zone()) Operator1<SpreadWithArityParameter>( // -- ConstructParameters parameters(arity, frequency, feedback);
return new (zone()) Operator1<ConstructParameters>( // --
IrOpcode::kJSConstructWithSpread, Operator::kNoProperties, // opcode IrOpcode::kJSConstructWithSpread, Operator::kNoProperties, // opcode
"JSConstructWithSpread", // name "JSConstructWithSpread", // name
parameters.arity(), 1, 1, 1, 1, 2, // counts parameters.arity(), 1, 1, 1, 1, 2, // counts
......
...@@ -128,7 +128,7 @@ ConstructForwardVarargsParameters const& ConstructForwardVarargsParametersOf( ...@@ -128,7 +128,7 @@ ConstructForwardVarargsParameters const& ConstructForwardVarargsParametersOf(
Operator const*) WARN_UNUSED_RESULT; Operator const*) WARN_UNUSED_RESULT;
// Defines the arity and the feedback for a JavaScript constructor call. This is // Defines the arity and the feedback for a JavaScript constructor call. This is
// used as a parameter by JSConstruct operators. // used as a parameter by JSConstruct and JSConstructWithSpread operators.
class ConstructParameters final { class ConstructParameters final {
public: public:
ConstructParameters(uint32_t arity, CallFrequency frequency, ConstructParameters(uint32_t arity, CallFrequency frequency,
...@@ -154,30 +154,6 @@ std::ostream& operator<<(std::ostream&, ConstructParameters const&); ...@@ -154,30 +154,6 @@ std::ostream& operator<<(std::ostream&, ConstructParameters const&);
ConstructParameters const& ConstructParametersOf(Operator const*); ConstructParameters const& ConstructParametersOf(Operator const*);
// Defines the arity for JavaScript calls with a spread as the last
// parameter. This is used as a parameter by JSConstructWithSpread and
// JSCallWithSpread operators.
class SpreadWithArityParameter final {
public:
explicit SpreadWithArityParameter(uint32_t arity) : arity_(arity) {}
uint32_t arity() const { return arity_; }
private:
uint32_t const arity_;
};
bool operator==(SpreadWithArityParameter const&,
SpreadWithArityParameter const&);
bool operator!=(SpreadWithArityParameter const&,
SpreadWithArityParameter const&);
size_t hash_value(SpreadWithArityParameter const&);
std::ostream& operator<<(std::ostream&, SpreadWithArityParameter const&);
SpreadWithArityParameter const& SpreadWithArityParameterOf(Operator const*);
// Defines the flags for a JavaScript call forwarding parameters. This // Defines the flags for a JavaScript call forwarding parameters. This
// is used as parameter by JSCallForwardVarargs operators. // is used as parameter by JSCallForwardVarargs operators.
class CallForwardVarargsParameters final { class CallForwardVarargsParameters final {
...@@ -213,7 +189,7 @@ CallForwardVarargsParameters const& CallForwardVarargsParametersOf( ...@@ -213,7 +189,7 @@ CallForwardVarargsParameters const& CallForwardVarargsParametersOf(
Operator const*) WARN_UNUSED_RESULT; Operator const*) WARN_UNUSED_RESULT;
// Defines the arity and the call flags for a JavaScript function call. This is // Defines the arity and the call flags for a JavaScript function call. This is
// used as a parameter by JSCall operators. // used as a parameter by JSCall and JSCallWithSpread operators.
class CallParameters final { class CallParameters final {
public: public:
CallParameters(size_t arity, CallFrequency frequency, CallParameters(size_t arity, CallFrequency frequency,
...@@ -672,7 +648,9 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -672,7 +648,9 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
VectorSlotPair const& feedback = VectorSlotPair(), VectorSlotPair const& feedback = VectorSlotPair(),
ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny); ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny);
const Operator* CallWithArrayLike(CallFrequency frequency); const Operator* CallWithArrayLike(CallFrequency frequency);
const Operator* CallWithSpread(uint32_t arity); const Operator* CallWithSpread(
uint32_t arity, CallFrequency frequency = CallFrequency(),
VectorSlotPair const& feedback = VectorSlotPair());
const Operator* CallRuntime(Runtime::FunctionId id); const Operator* CallRuntime(Runtime::FunctionId id);
const Operator* CallRuntime(Runtime::FunctionId id, size_t arity); const Operator* CallRuntime(Runtime::FunctionId id, size_t arity);
const Operator* CallRuntime(const Runtime::Function* function, size_t arity); const Operator* CallRuntime(const Runtime::Function* function, size_t arity);
...@@ -682,7 +660,9 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -682,7 +660,9 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
CallFrequency frequency = CallFrequency(), CallFrequency frequency = CallFrequency(),
VectorSlotPair const& feedback = VectorSlotPair()); VectorSlotPair const& feedback = VectorSlotPair());
const Operator* ConstructWithArrayLike(CallFrequency frequency); const Operator* ConstructWithArrayLike(CallFrequency frequency);
const Operator* ConstructWithSpread(uint32_t arity); const Operator* ConstructWithSpread(
uint32_t arity, CallFrequency frequency = CallFrequency(),
VectorSlotPair const& feedback = VectorSlotPair());
const Operator* ConvertReceiver(ConvertReceiverMode convert_mode); const Operator* ConvertReceiver(ConvertReceiverMode convert_mode);
......
...@@ -261,7 +261,8 @@ Reduction JSTypeHintLowering::ReduceCallOperation(const Operator* op, ...@@ -261,7 +261,8 @@ Reduction JSTypeHintLowering::ReduceCallOperation(const Operator* op,
int arg_count, Node* effect, int arg_count, Node* effect,
Node* control, Node* control,
FeedbackSlot slot) const { FeedbackSlot slot) const {
DCHECK_EQ(IrOpcode::kJSCall, op->opcode()); DCHECK(op->opcode() == IrOpcode::kJSCall ||
op->opcode() == IrOpcode::kJSCallWithSpread);
DCHECK(!slot.IsInvalid()); DCHECK(!slot.IsInvalid());
CallICNexus nexus(feedback_vector(), slot); CallICNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt( if (Node* node = TryBuildSoftDeopt(
...@@ -275,7 +276,8 @@ Reduction JSTypeHintLowering::ReduceCallOperation(const Operator* op, ...@@ -275,7 +276,8 @@ Reduction JSTypeHintLowering::ReduceCallOperation(const Operator* op,
Reduction JSTypeHintLowering::ReduceConstructOperation( Reduction JSTypeHintLowering::ReduceConstructOperation(
const Operator* op, Node* const* args, int arg_count, Node* effect, const Operator* op, Node* const* args, int arg_count, Node* effect,
Node* control, FeedbackSlot slot) const { Node* control, FeedbackSlot slot) const {
DCHECK_EQ(IrOpcode::kJSConstruct, op->opcode()); DCHECK(op->opcode() == IrOpcode::kJSConstruct ||
op->opcode() == IrOpcode::kJSConstructWithSpread);
DCHECK(!slot.IsInvalid()); DCHECK(!slot.IsInvalid());
CallICNexus nexus(feedback_vector(), slot); CallICNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt( if (Node* node = TryBuildSoftDeopt(
......
...@@ -1320,8 +1320,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallAnyReceiver(Register callable, ...@@ -1320,8 +1320,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallAnyReceiver(Register callable,
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::CallWithSpread(Register callable, BytecodeArrayBuilder& BytecodeArrayBuilder::CallWithSpread(Register callable,
RegisterList args) { RegisterList args,
OutputCallWithSpread(callable, args, args.register_count()); int feedback_slot) {
OutputCallWithSpread(callable, args, args.register_count(), feedback_slot);
return *this; return *this;
} }
...@@ -1333,8 +1334,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Construct(Register constructor, ...@@ -1333,8 +1334,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Construct(Register constructor,
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::ConstructWithSpread( BytecodeArrayBuilder& BytecodeArrayBuilder::ConstructWithSpread(
Register constructor, RegisterList args) { Register constructor, RegisterList args, int feedback_slot_id) {
OutputConstructWithSpread(constructor, args, args.register_count()); OutputConstructWithSpread(constructor, args, args.register_count(),
feedback_slot_id);
return *this; return *this;
} }
......
...@@ -264,7 +264,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final ...@@ -264,7 +264,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
// Call a JS function. The JSFunction or Callable to be called should be in // Call a JS function. The JSFunction or Callable to be called should be in
// |callable|, the receiver in |args[0]| and the arguments in |args[1]| // |callable|, the receiver in |args[0]| and the arguments in |args[1]|
// onwards. The final argument must be a spread. // onwards. The final argument must be a spread.
BytecodeArrayBuilder& CallWithSpread(Register callable, RegisterList args); BytecodeArrayBuilder& CallWithSpread(Register callable, RegisterList args,
int feedback_slot);
// Call the Construct operator. The accumulator holds the |new_target|. // Call the Construct operator. The accumulator holds the |new_target|.
// The |constructor| is in a register and arguments are in |args|. // The |constructor| is in a register and arguments are in |args|.
...@@ -275,7 +276,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final ...@@ -275,7 +276,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
// the |new_target|. The |constructor| is in a register and arguments are in // the |new_target|. The |constructor| is in a register and arguments are in
// |args|. The final argument must be a spread. // |args|. The final argument must be a spread.
BytecodeArrayBuilder& ConstructWithSpread(Register constructor, BytecodeArrayBuilder& ConstructWithSpread(Register constructor,
RegisterList args); RegisterList args,
int feedback_slot);
// Call the runtime function with |function_id| and arguments |args|. // Call the runtime function with |function_id| and arguments |args|.
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id, BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
......
...@@ -3216,7 +3216,7 @@ void BytecodeGenerator::VisitCall(Call* expr) { ...@@ -3216,7 +3216,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
if (is_spread_call) { if (is_spread_call) {
DCHECK(!implicit_undefined_receiver); DCHECK(!implicit_undefined_receiver);
builder()->CallWithSpread(callee, args); builder()->CallWithSpread(callee, args, feedback_slot_index);
} else if (call_type == Call::NAMED_PROPERTY_CALL || } else if (call_type == Call::NAMED_PROPERTY_CALL ||
call_type == Call::KEYED_PROPERTY_CALL) { call_type == Call::KEYED_PROPERTY_CALL) {
DCHECK(!implicit_undefined_receiver); DCHECK(!implicit_undefined_receiver);
...@@ -3247,9 +3247,9 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) { ...@@ -3247,9 +3247,9 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
// When a super call contains a spread, a CallSuper AST node is only created // When a super call contains a spread, a CallSuper AST node is only created
// if there is exactly one spread, and it is the last argument. // if there is exactly one spread, and it is the last argument.
int const feedback_slot_index = feedback_index(expr->CallFeedbackICSlot());
if (expr->only_last_arg_is_spread()) { if (expr->only_last_arg_is_spread()) {
// TODO(petermarshall): Collect type on the feedback slot. builder()->ConstructWithSpread(constructor, args_regs, feedback_slot_index);
builder()->ConstructWithSpread(constructor, args_regs);
} else { } else {
// Call construct. // Call construct.
// TODO(turbofan): For now we do gather feedback on super constructor // TODO(turbofan): For now we do gather feedback on super constructor
...@@ -3258,7 +3258,6 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) { ...@@ -3258,7 +3258,6 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
// is not an ideal solution for super constructor calls, but it gets // is not an ideal solution for super constructor calls, but it gets
// the job done for now. In the long run we might want to revisit this // the job done for now. In the long run we might want to revisit this
// and come up with a better way. // and come up with a better way.
int const feedback_slot_index = feedback_index(expr->CallFeedbackICSlot());
builder()->Construct(constructor, args_regs, feedback_slot_index); builder()->Construct(constructor, args_regs, feedback_slot_index);
} }
} }
...@@ -3273,12 +3272,11 @@ void BytecodeGenerator::VisitCallNew(CallNew* expr) { ...@@ -3273,12 +3272,11 @@ void BytecodeGenerator::VisitCallNew(CallNew* expr) {
builder()->SetExpressionPosition(expr); builder()->SetExpressionPosition(expr);
builder()->LoadAccumulatorWithRegister(constructor); builder()->LoadAccumulatorWithRegister(constructor);
int const feedback_slot_index = feedback_index(expr->CallNewFeedbackSlot());
if (expr->only_last_arg_is_spread()) { if (expr->only_last_arg_is_spread()) {
// TODO(petermarshall): Collect type on the feedback slot. builder()->ConstructWithSpread(constructor, args, feedback_slot_index);
builder()->ConstructWithSpread(constructor, args);
} else { } else {
builder()->Construct(constructor, args, builder()->Construct(constructor, args, feedback_slot_index);
feedback_index(expr->CallNewFeedbackSlot()));
} }
} }
......
...@@ -178,7 +178,7 @@ namespace interpreter { ...@@ -178,7 +178,7 @@ namespace interpreter {
V(CallUndefinedReceiver2, AccumulatorUse::kWrite, OperandType::kReg, \ V(CallUndefinedReceiver2, AccumulatorUse::kWrite, OperandType::kReg, \
OperandType::kReg, OperandType::kReg, OperandType::kIdx) \ OperandType::kReg, OperandType::kReg, OperandType::kIdx) \
V(CallWithSpread, AccumulatorUse::kWrite, OperandType::kReg, \ V(CallWithSpread, AccumulatorUse::kWrite, OperandType::kReg, \
OperandType::kRegList, OperandType::kRegCount) \ OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
V(CallRuntime, AccumulatorUse::kWrite, OperandType::kRuntimeId, \ V(CallRuntime, AccumulatorUse::kWrite, OperandType::kRuntimeId, \
OperandType::kRegList, OperandType::kRegCount) \ OperandType::kRegList, OperandType::kRegCount) \
V(CallRuntimeForPair, AccumulatorUse::kNone, OperandType::kRuntimeId, \ V(CallRuntimeForPair, AccumulatorUse::kNone, OperandType::kRuntimeId, \
...@@ -194,7 +194,7 @@ namespace interpreter { ...@@ -194,7 +194,7 @@ namespace interpreter {
V(Construct, AccumulatorUse::kReadWrite, OperandType::kReg, \ V(Construct, AccumulatorUse::kReadWrite, OperandType::kReg, \
OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \ OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
V(ConstructWithSpread, AccumulatorUse::kReadWrite, OperandType::kReg, \ V(ConstructWithSpread, AccumulatorUse::kReadWrite, OperandType::kReg, \
OperandType::kRegList, OperandType::kRegCount) \ OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
\ \
/* Test Operators */ \ /* Test Operators */ \
V(TestEqual, AccumulatorUse::kReadWrite, OperandType::kReg, \ V(TestEqual, AccumulatorUse::kReadWrite, OperandType::kReg, \
......
...@@ -559,6 +559,86 @@ Node* InterpreterAssembler::IncrementCallCount(Node* feedback_vector, ...@@ -559,6 +559,86 @@ Node* InterpreterAssembler::IncrementCallCount(Node* feedback_vector,
SKIP_WRITE_BARRIER); SKIP_WRITE_BARRIER);
} }
void InterpreterAssembler::CollectCallFeedback(Node* target, Node* context,
Node* feedback_vector,
Node* slot_id) {
// TODO(bmeurer): Add support for the Array constructor AllocationSite,
// and unify this with the general Call/Construct IC code below.
Label extra_checks(this, Label::kDeferred), done(this);
// Increment the call count.
IncrementCallCount(feedback_vector, slot_id);
// Check if we have monomorphic {target} feedback already.
Node* feedback_element = LoadFixedArrayElement(feedback_vector, slot_id);
Node* feedback_value = LoadWeakCellValueUnchecked(feedback_element);
Branch(WordEqual(target, feedback_value), &done, &extra_checks);
BIND(&extra_checks);
{
Label check_initialized(this), initialize(this), mark_megamorphic(this);
// Check if it is a megamorphic target.
Comment("check if megamorphic");
Node* is_megamorphic =
WordEqual(feedback_element,
HeapConstant(FeedbackVector::MegamorphicSentinel(isolate())));
GotoIf(is_megamorphic, &done);
Comment("check if weak cell");
Node* is_weak_cell = WordEqual(LoadMap(feedback_element),
LoadRoot(Heap::kWeakCellMapRootIndex));
GotoIfNot(is_weak_cell, &check_initialized);
// If the weak cell is cleared, we have a new chance to become monomorphic.
Comment("check if weak cell is cleared");
Node* is_smi = TaggedIsSmi(feedback_value);
Branch(is_smi, &initialize, &mark_megamorphic);
BIND(&check_initialized);
{
// Check if it is uninitialized.
Comment("check if uninitialized");
Node* is_uninitialized = WordEqual(
feedback_element, LoadRoot(Heap::kuninitialized_symbolRootIndex));
Branch(is_uninitialized, &initialize, &mark_megamorphic);
}
BIND(&initialize);
{
// Check if {target} is a JSFunction in the current native context.
Comment("check if function in same native context");
GotoIf(TaggedIsSmi(target), &mark_megamorphic);
// TODO(bmeurer): Add support for arbitrary callables here, and
// check via GetFunctionRealm (see src/objects.cc).
GotoIfNot(IsJSFunction(target), &mark_megamorphic);
Node* target_context =
LoadObjectField(target, JSFunction::kContextOffset);
Node* target_native_context = LoadNativeContext(target_context);
GotoIfNot(WordEqual(LoadNativeContext(context), target_native_context),
&mark_megamorphic);
CreateWeakCellInFeedbackVector(feedback_vector, SmiTag(slot_id), target);
Goto(&done);
}
BIND(&mark_megamorphic);
{
// MegamorphicSentinel is an immortal immovable object so
// write-barrier is not needed.
Comment("transition to megamorphic");
DCHECK(Heap::RootIsImmortalImmovable(Heap::kmegamorphic_symbolRootIndex));
StoreFixedArrayElement(
feedback_vector, slot_id,
HeapConstant(FeedbackVector::MegamorphicSentinel(isolate())),
SKIP_WRITE_BARRIER);
Goto(&done);
}
}
BIND(&done);
}
Node* InterpreterAssembler::CallJSWithFeedback( Node* InterpreterAssembler::CallJSWithFeedback(
compiler::Node* function, compiler::Node* context, compiler::Node* function, compiler::Node* context,
compiler::Node* first_arg, compiler::Node* arg_count, compiler::Node* first_arg, compiler::Node* arg_count,
...@@ -750,9 +830,13 @@ Node* InterpreterAssembler::CallJS(Node* function, Node* context, ...@@ -750,9 +830,13 @@ Node* InterpreterAssembler::CallJS(Node* function, Node* context,
} }
Node* InterpreterAssembler::CallJSWithSpread(Node* function, Node* context, Node* InterpreterAssembler::CallJSWithSpread(Node* function, Node* context,
Node* first_arg, Node* arg_count) { Node* first_arg, Node* arg_count,
Node* slot_id,
Node* feedback_vector) {
DCHECK(Bytecodes::MakesCallAlongCriticalPath(bytecode_)); DCHECK(Bytecodes::MakesCallAlongCriticalPath(bytecode_));
DCHECK_EQ(Bytecodes::GetReceiverMode(bytecode_), ConvertReceiverMode::kAny); DCHECK_EQ(Bytecodes::GetReceiverMode(bytecode_), ConvertReceiverMode::kAny);
CollectCallFeedback(function, context, feedback_vector, slot_id);
Comment("call using CallWithSpread builtin");
Callable callable = CodeFactory::InterpreterPushArgsThenCall( Callable callable = CodeFactory::InterpreterPushArgsThenCall(
isolate(), ConvertReceiverMode::kAny, isolate(), ConvertReceiverMode::kAny,
InterpreterPushArgsMode::kWithFinalSpread); InterpreterPushArgsMode::kWithFinalSpread);
...@@ -914,18 +998,16 @@ Node* InterpreterAssembler::Construct(Node* constructor, Node* context, ...@@ -914,18 +998,16 @@ Node* InterpreterAssembler::Construct(Node* constructor, Node* context,
Node* InterpreterAssembler::ConstructWithSpread(Node* constructor, Node* InterpreterAssembler::ConstructWithSpread(Node* constructor,
Node* context, Node* new_target, Node* context, Node* new_target,
Node* first_arg, Node* first_arg,
Node* arg_count) { Node* arg_count, Node* slot_id,
Node* feedback_vector) {
DCHECK(Bytecodes::MakesCallAlongCriticalPath(bytecode_)); DCHECK(Bytecodes::MakesCallAlongCriticalPath(bytecode_));
Variable return_value(this, MachineRepresentation::kTagged); CollectCallFeedback(constructor, context, feedback_vector, slot_id);
Comment("call using ConstructWithSpread"); Comment("call using ConstructWithSpread builtin");
Callable callable = CodeFactory::InterpreterPushArgsThenConstruct( Callable callable = CodeFactory::InterpreterPushArgsThenConstruct(
isolate(), InterpreterPushArgsMode::kWithFinalSpread); isolate(), InterpreterPushArgsMode::kWithFinalSpread);
Node* code_target = HeapConstant(callable.code()); Node* code_target = HeapConstant(callable.code());
return_value.Bind(CallStub(callable.descriptor(), code_target, context, return CallStub(callable.descriptor(), code_target, context, arg_count,
arg_count, new_target, constructor, new_target, constructor, UndefinedConstant(), first_arg);
UndefinedConstant(), first_arg));
return return_value.value();
} }
Node* InterpreterAssembler::CallRuntimeN(Node* function_id, Node* context, Node* InterpreterAssembler::CallRuntimeN(Node* function_id, Node* context,
......
...@@ -117,6 +117,12 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler { ...@@ -117,6 +117,12 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
compiler::Node* IncrementCallCount(compiler::Node* feedback_vector, compiler::Node* IncrementCallCount(compiler::Node* feedback_vector,
compiler::Node* slot_id); compiler::Node* slot_id);
// Collect CALL_IC feedback for |target| function in the
// |feedback_vector| at |slot_id|.
void CollectCallFeedback(compiler::Node* target, compiler::Node* context,
compiler::Node* slot_id,
compiler::Node* feedback_vector);
// Call JSFunction or Callable |function| with |arg_count| arguments (not // Call JSFunction or Callable |function| with |arg_count| arguments (not
// including receiver) and the first argument located at |first_arg|. Type // including receiver) and the first argument located at |first_arg|. Type
// feedback is collected in the slot at index |slot_id|. // feedback is collected in the slot at index |slot_id|.
...@@ -145,7 +151,9 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler { ...@@ -145,7 +151,9 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
compiler::Node* CallJSWithSpread(compiler::Node* function, compiler::Node* CallJSWithSpread(compiler::Node* function,
compiler::Node* context, compiler::Node* context,
compiler::Node* first_arg, compiler::Node* first_arg,
compiler::Node* arg_count); compiler::Node* arg_count,
compiler::Node* slot_id,
compiler::Node* feedback_vector);
// Call constructor |constructor| with |arg_count| arguments (not // Call constructor |constructor| with |arg_count| arguments (not
// including receiver) and the first argument located at // including receiver) and the first argument located at
...@@ -166,7 +174,9 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler { ...@@ -166,7 +174,9 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
compiler::Node* context, compiler::Node* context,
compiler::Node* new_target, compiler::Node* new_target,
compiler::Node* first_arg, compiler::Node* first_arg,
compiler::Node* arg_count); compiler::Node* arg_count,
compiler::Node* slot_id,
compiler::Node* feedback_vector);
// Call runtime function with |arg_count| arguments and the first argument // Call runtime function with |arg_count| arguments and the first argument
// located at |first_arg|. // located at |first_arg|.
......
...@@ -1912,10 +1912,13 @@ IGNITION_HANDLER(CallWithSpread, InterpreterAssembler) { ...@@ -1912,10 +1912,13 @@ IGNITION_HANDLER(CallWithSpread, InterpreterAssembler) {
Node* receiver_args_count = BytecodeOperandCount(2); Node* receiver_args_count = BytecodeOperandCount(2);
Node* receiver_count = Int32Constant(1); Node* receiver_count = Int32Constant(1);
Node* args_count = Int32Sub(receiver_args_count, receiver_count); Node* args_count = Int32Sub(receiver_args_count, receiver_count);
Node* slot_id = BytecodeOperandIdx(3);
Node* feedback_vector = LoadFeedbackVector();
Node* context = GetContext(); Node* context = GetContext();
// Call into Runtime function CallWithSpread which does everything. // Call into Runtime function CallWithSpread which does everything.
Node* result = CallJSWithSpread(callable, context, receiver_arg, args_count); Node* result = CallJSWithSpread(callable, context, receiver_arg, args_count,
slot_id, feedback_vector);
SetAccumulator(result); SetAccumulator(result);
Dispatch(); Dispatch();
} }
...@@ -1933,9 +1936,12 @@ IGNITION_HANDLER(ConstructWithSpread, InterpreterAssembler) { ...@@ -1933,9 +1936,12 @@ IGNITION_HANDLER(ConstructWithSpread, InterpreterAssembler) {
Node* first_arg_reg = BytecodeOperandReg(1); Node* first_arg_reg = BytecodeOperandReg(1);
Node* first_arg = RegisterLocation(first_arg_reg); Node* first_arg = RegisterLocation(first_arg_reg);
Node* args_count = BytecodeOperandCount(2); Node* args_count = BytecodeOperandCount(2);
Node* slot_id = BytecodeOperandIdx(3);
Node* feedback_vector = LoadFeedbackVector();
Node* context = GetContext(); Node* context = GetContext();
Node* result = ConstructWithSpread(constructor, context, new_target, Node* result =
first_arg, args_count); ConstructWithSpread(constructor, context, new_target, first_arg,
args_count, slot_id, feedback_vector);
SetAccumulator(result); SetAccumulator(result);
Dispatch(); Dispatch();
} }
......
...@@ -11,7 +11,7 @@ snippet: " ...@@ -11,7 +11,7 @@ snippet: "
" "
frame size: 3 frame size: 3
parameter count: 1 parameter count: 1
bytecode array length: 24 bytecode array length: 25
bytecodes: [ bytecodes: [
/* 30 E> */ B(StackCheck), /* 30 E> */ B(StackCheck),
/* 34 S> */ B(LdaGlobal), U8(0), U8(5), /* 34 S> */ B(LdaGlobal), U8(0), U8(5),
...@@ -20,7 +20,7 @@ bytecodes: [ ...@@ -20,7 +20,7 @@ bytecodes: [
B(Star), R(0), B(Star), R(0),
B(CreateArrayLiteral), U8(2), U8(9), U8(37), B(CreateArrayLiteral), U8(2), U8(9), U8(37),
B(Star), R(2), B(Star), R(2),
/* 39 E> */ B(CallWithSpread), R(0), R(1), U8(2), /* 39 E> */ B(CallWithSpread), R(0), R(1), U8(2), U8(3),
B(LdaUndefined), B(LdaUndefined),
/* 58 S> */ B(Return), /* 58 S> */ B(Return),
] ]
...@@ -38,7 +38,7 @@ snippet: " ...@@ -38,7 +38,7 @@ snippet: "
" "
frame size: 4 frame size: 4
parameter count: 1 parameter count: 1
bytecode array length: 27 bytecode array length: 28
bytecodes: [ bytecodes: [
/* 30 E> */ B(StackCheck), /* 30 E> */ B(StackCheck),
/* 34 S> */ B(LdaGlobal), U8(0), U8(5), /* 34 S> */ B(LdaGlobal), U8(0), U8(5),
...@@ -49,7 +49,7 @@ bytecodes: [ ...@@ -49,7 +49,7 @@ bytecodes: [
B(Star), R(2), B(Star), R(2),
B(CreateArrayLiteral), U8(2), U8(9), U8(37), B(CreateArrayLiteral), U8(2), U8(9), U8(37),
B(Star), R(3), B(Star), R(3),
/* 39 E> */ B(CallWithSpread), R(0), R(1), U8(3), /* 39 E> */ B(CallWithSpread), R(0), R(1), U8(3), U8(3),
B(LdaUndefined), B(LdaUndefined),
/* 61 S> */ B(Return), /* 61 S> */ B(Return),
] ]
......
...@@ -12,7 +12,7 @@ snippet: " ...@@ -12,7 +12,7 @@ snippet: "
" "
frame size: 7 frame size: 7
parameter count: 1 parameter count: 1
bytecode array length: 56 bytecode array length: 57
bytecodes: [ bytecodes: [
/* 30 E> */ B(StackCheck), /* 30 E> */ B(StackCheck),
B(CreateClosure), U8(0), U8(3), U8(2), B(CreateClosure), U8(0), U8(3), U8(2),
...@@ -33,7 +33,7 @@ bytecodes: [ ...@@ -33,7 +33,7 @@ bytecodes: [
/* 89 S> */ B(CreateArrayLiteral), U8(1), U8(6), U8(37), /* 89 S> */ B(CreateArrayLiteral), U8(1), U8(6), U8(37),
B(Star), R(3), B(Star), R(3),
B(Ldar), R(1), B(Ldar), R(1),
/* 89 E> */ B(ConstructWithSpread), R(1), R(3), U8(1), /* 89 E> */ B(ConstructWithSpread), R(1), R(3), U8(1), U8(4),
B(LdaUndefined), B(LdaUndefined),
/* 110 S> */ B(Return), /* 110 S> */ B(Return),
] ]
...@@ -51,7 +51,7 @@ snippet: " ...@@ -51,7 +51,7 @@ snippet: "
" "
frame size: 7 frame size: 7
parameter count: 1 parameter count: 1
bytecode array length: 59 bytecode array length: 60
bytecodes: [ bytecodes: [
/* 30 E> */ B(StackCheck), /* 30 E> */ B(StackCheck),
B(CreateClosure), U8(0), U8(3), U8(2), B(CreateClosure), U8(0), U8(3), U8(2),
...@@ -74,7 +74,7 @@ bytecodes: [ ...@@ -74,7 +74,7 @@ bytecodes: [
B(CreateArrayLiteral), U8(1), U8(6), U8(37), B(CreateArrayLiteral), U8(1), U8(6), U8(37),
B(Star), R(4), B(Star), R(4),
B(Ldar), R(1), B(Ldar), R(1),
/* 89 E> */ B(ConstructWithSpread), R(1), R(3), U8(2), /* 89 E> */ B(ConstructWithSpread), R(1), R(3), U8(2), U8(4),
B(LdaUndefined), B(LdaUndefined),
/* 113 S> */ B(Return), /* 113 S> */ B(Return),
] ]
......
...@@ -19,7 +19,7 @@ snippet: " ...@@ -19,7 +19,7 @@ snippet: "
" "
frame size: 4 frame size: 4
parameter count: 1 parameter count: 1
bytecode array length: 23 bytecode array length: 24
bytecodes: [ bytecodes: [
B(CreateRestParameter), B(CreateRestParameter),
B(Star), R(2), B(Star), R(2),
...@@ -30,7 +30,7 @@ bytecodes: [ ...@@ -30,7 +30,7 @@ bytecodes: [
/* 93 S> */ B(Ldar), R(1), /* 93 S> */ B(Ldar), R(1),
B(GetSuperConstructor), R(3), B(GetSuperConstructor), R(3),
B(Ldar), R(0), B(Ldar), R(0),
/* 93 E> */ B(ConstructWithSpread), R(3), R(2), U8(1), /* 93 E> */ B(ConstructWithSpread), R(3), R(2), U8(1), U8(3),
/* 93 S> */ B(Return), /* 93 S> */ B(Return),
] ]
constant pool: [ constant pool: [
...@@ -53,7 +53,7 @@ snippet: " ...@@ -53,7 +53,7 @@ snippet: "
" "
frame size: 7 frame size: 7
parameter count: 1 parameter count: 1
bytecode array length: 44 bytecode array length: 45
bytecodes: [ bytecodes: [
B(CreateRestParameter), B(CreateRestParameter),
B(Star), R(2), B(Star), R(2),
...@@ -68,7 +68,7 @@ bytecodes: [ ...@@ -68,7 +68,7 @@ bytecodes: [
B(Star), R(5), B(Star), R(5),
B(Ldar), R(0), B(Ldar), R(0),
B(Mov), R(2), R(6), B(Mov), R(2), R(6),
/* 140 E> */ B(ConstructWithSpread), R(4), R(5), U8(2), /* 140 E> */ B(ConstructWithSpread), R(4), R(5), U8(2), U8(3),
B(Star), R(4), B(Star), R(4),
B(Ldar), R(this), B(Ldar), R(this),
/* 140 E> */ B(ThrowSuperAlreadyCalledIfNotHole), /* 140 E> */ B(ThrowSuperAlreadyCalledIfNotHole),
......
...@@ -158,7 +158,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -158,7 +158,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CallRuntime(Runtime::kIsArray, reg) .CallRuntime(Runtime::kIsArray, reg)
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg_list, pair) .CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg_list, pair)
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg_list) .CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg_list)
.CallWithSpread(reg, reg_list); .CallWithSpread(reg, reg_list, 1);
// Emit binary operator invocations. // Emit binary operator invocations.
builder.BinaryOperation(Token::Value::ADD, reg, 1) builder.BinaryOperation(Token::Value::ADD, reg, 1)
...@@ -203,7 +203,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -203,7 +203,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.Delete(reg, LanguageMode::SLOPPY).Delete(reg, LanguageMode::STRICT); builder.Delete(reg, LanguageMode::SLOPPY).Delete(reg, LanguageMode::STRICT);
// Emit construct. // Emit construct.
builder.Construct(reg, reg_list, 1).ConstructWithSpread(reg, reg_list); builder.Construct(reg, reg_list, 1).ConstructWithSpread(reg, reg_list, 1);
// Emit test operator invocations. // Emit test operator invocations.
builder.CompareOperation(Token::Value::EQ, reg, 1) builder.CompareOperation(Token::Value::EQ, reg, 1)
......
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