Commit 2b236e33 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[nci] Add feedback input to Call nodes

This is likely the major change of the series, as Call nodes are the
focus of call reducer (and to a lesser extent other phases like
inlining).

This CL essentially adds the new input to Call nodes, and updates the
rest of the pipeline. As a (fairly large) drive-by, I also introduce
the JSCallNode wrapper class and apply it in call reducer.

This change, although large, will hopefully make future refactorings
*much* easier, since it is now clear where certain assumptions about
Call node layout are made.

Bug: v8:8888
Change-Id: Ia15fe0ba459b6034863a5815a4e4662cee41fc83
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2264353
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68616}
parent cd16e727
This diff is collapsed.
This diff is collapsed.
...@@ -216,7 +216,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer { ...@@ -216,7 +216,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
void CheckIfElementsKind(Node* receiver_elements_kind, ElementsKind kind, void CheckIfElementsKind(Node* receiver_elements_kind, ElementsKind kind,
Node* control, Node** if_true, Node** if_false); Node* control, Node** if_true, Node** if_false);
Node* LoadReceiverElementsKind(Node* receiver, Node** effect, Node** control); Node* LoadReceiverElementsKind(Node* receiver, Effect* effect,
Control control);
bool IsBuiltinOrApiFunction(JSFunctionRef target_ref) const; bool IsBuiltinOrApiFunction(JSFunctionRef target_ref) const;
......
...@@ -935,10 +935,14 @@ void JSGenericLowering::LowerJSCallForwardVarargs(Node* node) { ...@@ -935,10 +935,14 @@ void JSGenericLowering::LowerJSCallForwardVarargs(Node* node) {
} }
void JSGenericLowering::LowerJSCall(Node* node) { void JSGenericLowering::LowerJSCall(Node* node) {
CallParameters const& p = CallParametersOf(node->op()); JSCallNode n(node);
CallParameters const& p = n.Parameters();
int const arg_count = p.arity_without_implicit_args(); int const arg_count = p.arity_without_implicit_args();
ConvertReceiverMode const mode = p.convert_mode(); ConvertReceiverMode const mode = p.convert_mode();
Node* feedback_vector = n.feedback_vector();
node->RemoveInput(n.FeedbackVectorIndex());
if (CollectFeedbackInGenericLowering() && p.feedback().IsValid()) { if (CollectFeedbackInGenericLowering() && p.feedback().IsValid()) {
Callable callable = CodeFactory::Call_WithFeedback(isolate(), mode); Callable callable = CodeFactory::Call_WithFeedback(isolate(), mode);
CallDescriptor::Flags flags = FrameStateFlagForCall(node); CallDescriptor::Flags flags = FrameStateFlagForCall(node);
...@@ -946,7 +950,6 @@ void JSGenericLowering::LowerJSCall(Node* node) { ...@@ -946,7 +950,6 @@ void JSGenericLowering::LowerJSCall(Node* node) {
zone(), callable.descriptor(), arg_count + 1, flags); zone(), callable.descriptor(), arg_count + 1, flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code()); Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(arg_count); Node* stub_arity = jsgraph()->Int32Constant(arg_count);
Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector);
Node* slot = jsgraph()->Int32Constant(p.feedback().index()); Node* slot = jsgraph()->Int32Constant(p.feedback().index());
node->InsertInput(zone(), 0, stub_code); node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, stub_arity); node->InsertInput(zone(), 2, stub_arity);
......
...@@ -50,36 +50,44 @@ class JSCallAccessor { ...@@ -50,36 +50,44 @@ class JSCallAccessor {
call->opcode() == IrOpcode::kJSConstruct); call->opcode() == IrOpcode::kJSConstruct);
} }
Node* target() { Node* target() const {
// Both, {JSCall} and {JSConstruct}, have same layout here. // Both, {JSCall} and {JSConstruct}, have same layout here.
return call_->InputAt(0); return call_->InputAt(0);
} }
Node* receiver() { Node* receiver() const {
DCHECK_EQ(IrOpcode::kJSCall, call_->opcode()); DCHECK_EQ(IrOpcode::kJSCall, call_->opcode());
return call_->InputAt(1); return JSCallNode{call_}.receiver();
} }
Node* new_target() { Node* new_target() const {
DCHECK_EQ(IrOpcode::kJSConstruct, call_->opcode()); DCHECK_EQ(IrOpcode::kJSConstruct, call_->opcode());
return call_->InputAt(formal_arguments() + 1); return call_->InputAt(formal_arguments() + 1);
} }
Node* frame_state() { Node* argument(int i) const {
// Both, {JSCall} and {JSConstruct}, have same layout here.
static constexpr int kFirstArgumentIndex = 2;
STATIC_ASSERT(kFirstArgumentIndex == JSCallNode::FirstArgumentIndex());
return call_->InputAt(kFirstArgumentIndex + i);
}
Node* frame_state() const {
// Both, {JSCall} and {JSConstruct}, have frame state. // Both, {JSCall} and {JSConstruct}, have frame state.
return NodeProperties::GetFrameStateInput(call_); return NodeProperties::GetFrameStateInput(call_);
} }
int formal_arguments() { int formal_arguments() const {
// Both, {JSCall} and {JSConstruct}, have two extra inputs: // TODO(jgruber): Remove once the JSConstructNode wrapper is
// - JSConstruct: Includes target function and new target. // implemented.
// - JSCall: Includes target function and receiver. return (call_->opcode() == IrOpcode::kJSCall)
return call_->op()->ValueInputCount() - 2; ? JSCallNode{call_}.ArgumentCount()
: call_->op()->ValueInputCount() - kTargetAndNewTarget;
} }
CallFrequency const& frequency() const { CallFrequency const& frequency() const {
return (call_->opcode() == IrOpcode::kJSCall) return (call_->opcode() == IrOpcode::kJSCall)
? CallParametersOf(call_->op()).frequency() ? JSCallNode{call_}.Parameters().frequency()
: ConstructParametersOf(call_->op()).frequency(); : ConstructParametersOf(call_->op()).frequency();
} }
...@@ -106,7 +114,13 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context, ...@@ -106,7 +114,13 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
// {inliner_inputs} counts JSFunction, receiver, arguments, but not // {inliner_inputs} counts JSFunction, receiver, arguments, but not
// new target value, argument count, context, effect or control. // new target value, argument count, context, effect or control.
int inliner_inputs = call->op()->ValueInputCount(); // TODO(jgruber): Refactor this once JSConstructNode is implemented.
STATIC_ASSERT(kTargetAndNewTarget == 2);
const bool inliner_is_call = call->opcode() == IrOpcode::kJSCall;
const int inliner_inputs = inliner_is_call
? call->op()->ValueInputCount() -
JSCallNode::kFeedbackVectorInputCount
: call->op()->ValueInputCount();
// Iterate over all uses of the start node. // Iterate over all uses of the start node.
for (Edge edge : start->use_edges()) { for (Edge edge : start->use_edges()) {
Node* use = edge.from(); Node* use = edge.from();
...@@ -123,6 +137,9 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context, ...@@ -123,6 +137,9 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
Replace(use, new_target); Replace(use, new_target);
} else if (index == inlinee_arity_index) { } else if (index == inlinee_arity_index) {
// The projection is requesting the number of arguments. // The projection is requesting the number of arguments.
STATIC_ASSERT(JSCallNode::kExtraInputCount ==
JSCallNode::kFeedbackVectorInputCount + 2);
STATIC_ASSERT(kTargetAndNewTarget == 2);
Replace(use, jsgraph()->Constant(inliner_inputs - 2)); Replace(use, jsgraph()->Constant(inliner_inputs - 2));
} else if (index == inlinee_context_index) { } else if (index == inlinee_context_index) {
// The projection is requesting the inlinee function context. // The projection is requesting the inlinee function context.
...@@ -240,31 +257,36 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state, ...@@ -240,31 +257,36 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
FrameStateType frame_state_type, FrameStateType frame_state_type,
SharedFunctionInfoRef shared, SharedFunctionInfoRef shared,
Node* context) { Node* context) {
const int parameter_count_with_receiver =
parameter_count + JSCallNode::kReceiverInputCount;
const FrameStateFunctionInfo* state_info = const FrameStateFunctionInfo* state_info =
common()->CreateFrameStateFunctionInfo( common()->CreateFrameStateFunctionInfo(
frame_state_type, parameter_count + 1, 0, shared.object()); frame_state_type, parameter_count_with_receiver, 0, shared.object());
const Operator* op = common()->FrameState( const Operator* op = common()->FrameState(
bailout_id, OutputFrameStateCombine::Ignore(), state_info); bailout_id, OutputFrameStateCombine::Ignore(), state_info);
const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense()); const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
Node* node0 = graph()->NewNode(op0); Node* node0 = graph()->NewNode(op0);
static constexpr int kTargetInputIndex = 0; // Note how we refer to 'receiver' even though `node` may be a
static constexpr int kReceiverInputIndex = 1; // JSConstruct node. That's because the layout of construct nodes
const int parameter_count_with_receiver = parameter_count + 1; // has been modified to look like call nodes in `ReduceJSCall`; the
// `new_target` has been put into the receiver slot.
// TODO(jgruber): Either unify the layout of these two nodes, or
// refactor this to be more expected in some way.
JSCallAccessor call(node);
NodeVector params(local_zone_); NodeVector params(local_zone_);
for (int i = 0; i < parameter_count_with_receiver; i++) { params.push_back(node->InputAt(JSCallNode::ReceiverIndex()));
params.push_back(node->InputAt(kReceiverInputIndex + i)); for (int i = 0; i < parameter_count; i++) {
params.push_back(call.argument(i));
} }
const Operator* op_param = common()->StateValues( const Operator* op_param = common()->StateValues(
static_cast<int>(params.size()), SparseInputMask::Dense()); static_cast<int>(params.size()), SparseInputMask::Dense());
Node* params_node = graph()->NewNode( Node* params_node = graph()->NewNode(
op_param, static_cast<int>(params.size()), &params.front()); op_param, static_cast<int>(params.size()), &params.front());
if (!context) { if (context == nullptr) context = jsgraph()->UndefinedConstant();
context = jsgraph()->UndefinedConstant(); return graph()->NewNode(op, params_node, node0, node0, context, call.target(),
} outer_frame_state);
return graph()->NewNode(op, params_node, node0, node0, context,
node->InputAt(kTargetInputIndex), outer_frame_state);
} }
namespace { namespace {
...@@ -283,7 +305,7 @@ bool NeedsImplicitReceiver(SharedFunctionInfoRef shared_info) { ...@@ -283,7 +305,7 @@ bool NeedsImplicitReceiver(SharedFunctionInfoRef shared_info) {
base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget( base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget(
Node* node) { Node* node) {
DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
HeapObjectMatcher match(node->InputAt(0)); HeapObjectMatcher match(node->InputAt(JSCallNode::TargetIndex()));
// This reducer can handle both normal function calls as well a constructor // This reducer can handle both normal function calls as well a constructor
// calls whenever the target is a constant function object, as follows: // calls whenever the target is a constant function object, as follows:
...@@ -336,7 +358,7 @@ base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget( ...@@ -336,7 +358,7 @@ base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget(
FeedbackVectorRef JSInliner::DetermineCallContext(Node* node, FeedbackVectorRef JSInliner::DetermineCallContext(Node* node,
Node** context_out) { Node** context_out) {
DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
HeapObjectMatcher match(node->InputAt(0)); HeapObjectMatcher match(node->InputAt(JSCallNode::TargetIndex()));
if (match.HasValue() && match.Ref(broker()).IsJSFunction()) { if (match.HasValue() && match.Ref(broker()).IsJSFunction()) {
JSFunctionRef function = match.Ref(broker()).AsJSFunction(); JSFunctionRef function = match.Ref(broker()).AsJSFunction();
...@@ -507,8 +529,10 @@ Reduction JSInliner::ReduceJSCall(Node* node) { ...@@ -507,8 +529,10 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
// normal {JSCall} node so that the rest of the inlining machinery // normal {JSCall} node so that the rest of the inlining machinery
// behaves as if we were dealing with a regular function invocation. // behaves as if we were dealing with a regular function invocation.
new_target = call.new_target(); // Retrieve new target value input. new_target = call.new_target(); // Retrieve new target value input.
// TODO(jgruber,v8:8888): Update when JSConstruct nodes also gain a
// feedback vector input.
node->RemoveInput(call.formal_arguments() + 1); // Drop new target. node->RemoveInput(call.formal_arguments() + 1); // Drop new target.
node->InsertInput(graph()->zone(), 1, new_target); node->InsertInput(graph()->zone(), JSCallNode::ReceiverIndex(), new_target);
// Insert nodes around the call that model the behavior required for a // Insert nodes around the call that model the behavior required for a
// constructor dispatch (allocate implicit receiver and check return value). // constructor dispatch (allocate implicit receiver and check return value).
...@@ -575,7 +599,7 @@ Reduction JSInliner::ReduceJSCall(Node* node) { ...@@ -575,7 +599,7 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
// Fix input destroyed by the above {ReplaceWithValue} call. // Fix input destroyed by the above {ReplaceWithValue} call.
NodeProperties::ReplaceControlInput(branch_is_receiver, node_success, 0); NodeProperties::ReplaceControlInput(branch_is_receiver, node_success, 0);
} }
node->ReplaceInput(1, receiver); node->ReplaceInput(JSCallNode::ReceiverIndex(), receiver);
// Insert a construct stub frame into the chain of frame states. This will // Insert a construct stub frame into the chain of frame states. This will
// reconstruct the proper frame when deoptimizing within the constructor. // reconstruct the proper frame when deoptimizing within the constructor.
frame_state = CreateArtificialFrameState( frame_state = CreateArtificialFrameState(
...@@ -596,7 +620,8 @@ Reduction JSInliner::ReduceJSCall(Node* node) { ...@@ -596,7 +620,8 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
Node* receiver = effect = Node* receiver = effect =
graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()), graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
call.receiver(), global_proxy, effect, start); call.receiver(), global_proxy, effect, start);
NodeProperties::ReplaceValueInput(node, receiver, 1); NodeProperties::ReplaceValueInput(node, receiver,
JSCallNode::ReceiverIndex());
NodeProperties::ReplaceEffectInput(node, effect); NodeProperties::ReplaceEffectInput(node, effect);
} }
} }
......
...@@ -330,8 +330,14 @@ Reduction JSIntrinsicLowering::ReduceToString(Node* node) { ...@@ -330,8 +330,14 @@ Reduction JSIntrinsicLowering::ReduceToString(Node* node) {
Reduction JSIntrinsicLowering::ReduceCall(Node* node) { Reduction JSIntrinsicLowering::ReduceCall(Node* node) {
size_t const arity = CallRuntimeParametersOf(node->op()).arity(); int const arity =
NodeProperties::ChangeOp(node, javascript()->Call(arity)); static_cast<int>(CallRuntimeParametersOf(node->op()).arity());
static constexpr int kTargetAndReceiver = 2;
Node* feedback = jsgraph()->UndefinedConstant();
node->InsertInput(graph()->zone(), arity, feedback);
NodeProperties::ChangeOp(
node,
javascript()->Call(JSCallNode::ArityForArgc(arity - kTargetAndReceiver)));
return Changed(node); return Changed(node);
} }
......
...@@ -482,13 +482,20 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -482,13 +482,20 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// Call the @@hasInstance handler. // Call the @@hasInstance handler.
Node* target = jsgraph()->Constant(*constant); Node* target = jsgraph()->Constant(*constant);
node->InsertInput(graph()->zone(), 0, target); Node* feedback = jsgraph()->UndefinedConstant();
node->ReplaceInput(1, constructor); // Value inputs plus context, frame state, effect, control.
node->ReplaceInput(2, object); STATIC_ASSERT(JSCallNode::ArityForArgc(1) + 4 == 8);
node->ReplaceInput(4, continuation_frame_state); node->EnsureInputCount(graph()->zone(), 8);
node->ReplaceInput(5, effect); node->ReplaceInput(JSCallNode::TargetIndex(), target);
node->ReplaceInput(JSCallNode::ReceiverIndex(), constructor);
node->ReplaceInput(JSCallNode::ArgumentIndex(0), object);
node->ReplaceInput(3, feedback);
node->ReplaceInput(4, context);
node->ReplaceInput(5, continuation_frame_state);
node->ReplaceInput(6, effect);
node->ReplaceInput(7, control);
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, javascript()->Call(1 + kTargetAndReceiver, CallFrequency(), node, javascript()->Call(JSCallNode::ArityForArgc(1), CallFrequency(),
FeedbackSource(), FeedbackSource(),
ConvertReceiverMode::kNotNullOrUndefined)); ConvertReceiverMode::kNotNullOrUndefined));
...@@ -1430,10 +1437,11 @@ Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) { ...@@ -1430,10 +1437,11 @@ Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
? SpeculationMode::kDisallowSpeculation ? SpeculationMode::kDisallowSpeculation
: feedback.AsCall().speculation_mode(); : feedback.AsCall().speculation_mode();
const Operator* call_op = javascript()->Call( const Operator* call_op = javascript()->Call(
0 + kTargetAndReceiver, CallFrequency(), p.callFeedback(), JSCallNode::ArityForArgc(0), CallFrequency(), p.callFeedback(),
ConvertReceiverMode::kNotNullOrUndefined, mode, ConvertReceiverMode::kNotNullOrUndefined, mode,
CallFeedbackRelation::kRelated); CallFeedbackRelation::kRelated);
Node* call_property = graph()->NewNode(call_op, load_property, receiver, Node* call_property =
graph()->NewNode(call_op, load_property, receiver, n.feedback_vector(),
context, frame_state, effect, control); context, frame_state, effect, control);
return Replace(call_property); return Replace(call_property);
...@@ -2043,11 +2051,12 @@ Node* JSNativeContextSpecialization::InlinePropertyGetterCall( ...@@ -2043,11 +2051,12 @@ Node* JSNativeContextSpecialization::InlinePropertyGetterCall(
// Introduce the call to the getter function. // Introduce the call to the getter function.
Node* value; Node* value;
if (constant.IsJSFunction()) { if (constant.IsJSFunction()) {
Node* feedback = jsgraph()->UndefinedConstant();
value = *effect = *control = graph()->NewNode( value = *effect = *control = graph()->NewNode(
jsgraph()->javascript()->Call(0 + kTargetAndReceiver, CallFrequency(), jsgraph()->javascript()->Call(JSCallNode::ArityForArgc(0),
FeedbackSource(), CallFrequency(), FeedbackSource(),
ConvertReceiverMode::kNotNullOrUndefined), ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, context, frame_state, *effect, *control); target, receiver, feedback, context, frame_state, *effect, *control);
} else { } else {
Node* holder = access_info.holder().is_null() Node* holder = access_info.holder().is_null()
? receiver ? receiver
...@@ -2081,11 +2090,13 @@ void JSNativeContextSpecialization::InlinePropertySetterCall( ...@@ -2081,11 +2090,13 @@ void JSNativeContextSpecialization::InlinePropertySetterCall(
FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op()); FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op());
// Introduce the call to the setter function. // Introduce the call to the setter function.
if (constant.IsJSFunction()) { if (constant.IsJSFunction()) {
Node* feedback = jsgraph()->UndefinedConstant();
*effect = *control = graph()->NewNode( *effect = *control = graph()->NewNode(
jsgraph()->javascript()->Call(1 + kTargetAndReceiver, CallFrequency(), jsgraph()->javascript()->Call(JSCallNode::ArityForArgc(1),
FeedbackSource(), CallFrequency(), FeedbackSource(),
ConvertReceiverMode::kNotNullOrUndefined), ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, value, context, frame_state, *effect, *control); target, receiver, value, feedback, context, frame_state, *effect,
*control);
} else { } else {
Node* holder = access_info.holder().is_null() Node* holder = access_info.holder().is_null()
? receiver ? receiver
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <limits> #include <limits>
#include "src/base/lazy-instance.h" #include "src/base/lazy-instance.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/operator.h" #include "src/compiler/operator.h"
#include "src/handles/handles-inl.h" #include "src/handles/handles-inl.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
...@@ -27,6 +28,10 @@ constexpr Operator::Properties BinopProperties(Operator::Opcode opcode) { ...@@ -27,6 +28,10 @@ constexpr Operator::Properties BinopProperties(Operator::Opcode opcode) {
} // namespace } // namespace
TNode<Object> JSCallNode::ArgumentOrUndefined(int i, JSGraph* jsgraph) const {
return ArgumentOr(i, jsgraph->UndefinedConstant());
}
std::ostream& operator<<(std::ostream& os, CallFrequency const& f) { std::ostream& operator<<(std::ostream& os, CallFrequency const& f) {
if (f.IsUnknown()) return os << "unknown"; if (f.IsUnknown()) return os << "unknown";
return os << f.value(); return os << f.value();
...@@ -768,7 +773,7 @@ const Operator* JSOperatorBuilder::Call( ...@@ -768,7 +773,7 @@ const Operator* JSOperatorBuilder::Call(
FeedbackSource const& feedback, ConvertReceiverMode convert_mode, FeedbackSource const& feedback, ConvertReceiverMode convert_mode,
SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) { SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) {
CallParameters parameters(arity, frequency, feedback, convert_mode, CallParameters parameters(arity, frequency, feedback, convert_mode,
speculation_mode, feedback_relation); speculation_mode, feedback_relation, true);
return new (zone()) Operator1<CallParameters>( // -- return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCall, Operator::kNoProperties, // opcode IrOpcode::kJSCall, Operator::kNoProperties, // opcode
"JSCall", // name "JSCall", // name
...@@ -780,7 +785,7 @@ const Operator* JSOperatorBuilder::CallWithArrayLike( ...@@ -780,7 +785,7 @@ const Operator* JSOperatorBuilder::CallWithArrayLike(
const CallFrequency& frequency, const FeedbackSource& feedback, const CallFrequency& frequency, const FeedbackSource& feedback,
SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) { SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) {
CallParameters parameters(2, frequency, feedback, ConvertReceiverMode::kAny, CallParameters parameters(2, frequency, feedback, ConvertReceiverMode::kAny,
speculation_mode, feedback_relation); speculation_mode, feedback_relation, false);
return new (zone()) Operator1<CallParameters>( // -- return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCallWithArrayLike, Operator::kNoProperties, // opcode IrOpcode::kJSCallWithArrayLike, Operator::kNoProperties, // opcode
"JSCallWithArrayLike", // name "JSCallWithArrayLike", // name
...@@ -796,7 +801,7 @@ const Operator* JSOperatorBuilder::CallWithSpread( ...@@ -796,7 +801,7 @@ const Operator* JSOperatorBuilder::CallWithSpread(
feedback.IsValid()); feedback.IsValid());
CallParameters parameters(arity, frequency, feedback, CallParameters parameters(arity, frequency, feedback,
ConvertReceiverMode::kAny, speculation_mode, ConvertReceiverMode::kAny, speculation_mode,
feedback_relation); feedback_relation, false);
return new (zone()) Operator1<CallParameters>( // -- return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCallWithSpread, Operator::kNoProperties, // opcode IrOpcode::kJSCallWithSpread, Operator::kNoProperties, // opcode
"JSCallWithSpread", // name "JSCallWithSpread", // name
......
...@@ -28,6 +28,7 @@ class SharedFunctionInfo; ...@@ -28,6 +28,7 @@ class SharedFunctionInfo;
namespace compiler { namespace compiler {
// Forward declarations. // Forward declarations.
class JSGraph;
class Operator; class Operator;
struct JSOperatorGlobalCache; struct JSOperatorGlobalCache;
...@@ -218,7 +219,9 @@ CallForwardVarargsParameters const& CallForwardVarargsParametersOf( ...@@ -218,7 +219,9 @@ CallForwardVarargsParameters const& CallForwardVarargsParametersOf(
Operator const*) V8_WARN_UNUSED_RESULT; Operator const*) V8_WARN_UNUSED_RESULT;
// Part of CallParameters::arity. // Part of CallParameters::arity.
// TODO(jgruber): Fold this into JSCallNode.
static constexpr int kTargetAndReceiver = 2; static constexpr int kTargetAndReceiver = 2;
static constexpr int kTargetAndReceiverAndVector = 3;
// Defines the arity (parameters plus the target and receiver) and the call // Defines the arity (parameters plus the target and receiver) and the call
// flags for a JavaScript function call. This is used as a parameter by JSCall, // flags for a JavaScript function call. This is used as a parameter by JSCall,
...@@ -229,19 +232,21 @@ class CallParameters final { ...@@ -229,19 +232,21 @@ class CallParameters final {
FeedbackSource const& feedback, FeedbackSource const& feedback,
ConvertReceiverMode convert_mode, ConvertReceiverMode convert_mode,
SpeculationMode speculation_mode, SpeculationMode speculation_mode,
CallFeedbackRelation feedback_relation) CallFeedbackRelation feedback_relation, bool has_vector)
: bit_field_(ArityField::encode(arity) | : bit_field_(ArityField::encode(arity) |
CallFeedbackRelationField::encode(feedback_relation) | CallFeedbackRelationField::encode(feedback_relation) |
SpeculationModeField::encode(speculation_mode) | SpeculationModeField::encode(speculation_mode) |
ConvertReceiverModeField::encode(convert_mode)), ConvertReceiverModeField::encode(convert_mode)),
frequency_(frequency), frequency_(frequency),
feedback_(feedback) { feedback_(feedback),
has_vector_(has_vector) {
// CallFeedbackRelation is ignored if the feedback slot is invalid. // CallFeedbackRelation is ignored if the feedback slot is invalid.
DCHECK_IMPLIES(speculation_mode == SpeculationMode::kAllowSpeculation, DCHECK_IMPLIES(speculation_mode == SpeculationMode::kAllowSpeculation,
feedback.IsValid()); feedback.IsValid());
DCHECK_IMPLIES(!feedback.IsValid(), DCHECK_IMPLIES(!feedback.IsValid(),
feedback_relation == CallFeedbackRelation::kUnrelated); feedback_relation == CallFeedbackRelation::kUnrelated);
DCHECK_GE(arity, kTargetAndReceiver); DCHECK_GE(arity,
has_vector_ ? kTargetAndReceiverAndVector : kTargetAndReceiver);
DCHECK(is_int32(arity)); DCHECK(is_int32(arity));
} }
...@@ -249,7 +254,8 @@ class CallParameters final { ...@@ -249,7 +254,8 @@ class CallParameters final {
// without extra args in CallParameters. // without extra args in CallParameters.
size_t arity() const { return ArityField::decode(bit_field_); } size_t arity() const { return ArityField::decode(bit_field_); }
int arity_without_implicit_args() const { int arity_without_implicit_args() const {
return static_cast<int>(arity() - kTargetAndReceiver); return static_cast<int>(arity() - (has_vector_ ? kTargetAndReceiverAndVector
: kTargetAndReceiver));
} }
CallFrequency const& frequency() const { return frequency_; } CallFrequency const& frequency() const { return frequency_; }
...@@ -288,6 +294,8 @@ class CallParameters final { ...@@ -288,6 +294,8 @@ class CallParameters final {
uint32_t const bit_field_; uint32_t const bit_field_;
CallFrequency const frequency_; CallFrequency const frequency_;
FeedbackSource const feedback_; FeedbackSource const feedback_;
// TODO(jgruber): Remove once Call variants all have a feedback vector input.
bool has_vector_;
}; };
size_t hash_value(CallParameters const&); size_t hash_value(CallParameters const&);
...@@ -1214,6 +1222,61 @@ class JSStorePropertyNode final : public JSNodeWrapperBase { ...@@ -1214,6 +1222,61 @@ class JSStorePropertyNode final : public JSNodeWrapperBase {
#undef INPUTS #undef INPUTS
}; };
class JSCallNode final : public JSNodeWrapperBase {
public:
explicit constexpr JSCallNode(Node* node) : JSNodeWrapperBase(node) {
CONSTEXPR_DCHECK(node->opcode() == IrOpcode::kJSCall);
}
const CallParameters& Parameters() const {
return CallParametersOf(node()->op());
}
#define INPUTS(V) \
V(Target, target, 0, Object) \
V(Receiver, receiver, 1, Object)
INPUTS(DEFINE_INPUT_ACCESSORS)
#undef INPUTS
// Besides actual arguments, JSCall nodes also take:
static constexpr int kTargetInputCount = 1;
static constexpr int kReceiverInputCount = 1;
static constexpr int kFeedbackVectorInputCount = 1;
static constexpr int kExtraInputCount =
kTargetInputCount + kReceiverInputCount + kFeedbackVectorInputCount;
// This is the arity fed into CallArguments.
static constexpr int ArityForArgc(int parameters) {
return parameters + kExtraInputCount;
}
static constexpr int FirstArgumentIndex() { return ReceiverIndex() + 1; }
static constexpr int ArgumentIndex(int i) { return FirstArgumentIndex() + i; }
TNode<Object> Argument(int i) const {
DCHECK_LT(i, ArgumentCount());
return TNode<Object>::UncheckedCast(
NodeProperties::GetValueInput(node(), ArgumentIndex(i)));
}
TNode<Object> ArgumentOr(int i, Node* default_value) const {
return i < ArgumentCount() ? Argument(i)
: TNode<Object>::UncheckedCast(default_value);
}
TNode<Object> ArgumentOrUndefined(int i, JSGraph* jsgraph) const;
int ArgumentCount() const {
DCHECK_GE(node()->op()->ValueInputCount(), kExtraInputCount);
return node()->op()->ValueInputCount() - kExtraInputCount;
}
int FeedbackVectorIndex() const {
DCHECK_GE(node()->op()->ValueInputCount(), 1);
return node()->op()->ValueInputCount() - 1;
}
TNode<HeapObject> feedback_vector() const {
return TNode<HeapObject>::UncheckedCast(
NodeProperties::GetValueInput(node(), FeedbackVectorIndex()));
}
};
#undef DEFINE_INPUT_ACCESSORS #undef DEFINE_INPUT_ACCESSORS
} // namespace compiler } // namespace compiler
......
...@@ -1710,16 +1710,16 @@ Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) { ...@@ -1710,16 +1710,16 @@ Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) {
} }
Reduction JSTypedLowering::ReduceJSCall(Node* node) { Reduction JSTypedLowering::ReduceJSCall(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); JSCallNode n(node);
CallParameters const& p = CallParametersOf(node->op()); CallParameters const& p = n.Parameters();
int arity = p.arity_without_implicit_args(); int arity = p.arity_without_implicit_args();
ConvertReceiverMode convert_mode = p.convert_mode(); ConvertReceiverMode convert_mode = p.convert_mode();
Node* target = NodeProperties::GetValueInput(node, 0); Node* target = n.target();
Type target_type = NodeProperties::GetType(target); Type target_type = NodeProperties::GetType(target);
Node* receiver = NodeProperties::GetValueInput(node, 1); Node* receiver = n.receiver();
Type receiver_type = NodeProperties::GetType(receiver); Type receiver_type = NodeProperties::GetType(receiver);
Node* effect = NodeProperties::GetEffectInput(node); Effect effect = n.effect();
Node* control = NodeProperties::GetControlInput(node); Control control = n.control();
// Try to infer receiver {convert_mode} from {receiver} type. // Try to infer receiver {convert_mode} from {receiver} type.
if (receiver_type.Is(Type::NullOrUndefined())) { if (receiver_type.Is(Type::NullOrUndefined())) {
...@@ -1787,6 +1787,9 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) { ...@@ -1787,6 +1787,9 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
Node* new_target = jsgraph()->UndefinedConstant(); Node* new_target = jsgraph()->UndefinedConstant();
// The node will change operators, remove the feedback vector.
node->RemoveInput(n.FeedbackVectorIndex());
if (NeedsArgumentAdaptorFrame(*shared, arity)) { if (NeedsArgumentAdaptorFrame(*shared, arity)) {
// Check if it's safe to skip the arguments adaptor for {shared}, // Check if it's safe to skip the arguments adaptor for {shared},
// that is whether the target function anyways cannot observe the // that is whether the target function anyways cannot observe the
...@@ -1865,6 +1868,8 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) { ...@@ -1865,6 +1868,8 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
// Check if {target} is a JSFunction. // Check if {target} is a JSFunction.
if (target_type.Is(Type::Function())) { if (target_type.Is(Type::Function())) {
// The node will change operators, remove the feedback vector.
node->RemoveInput(n.FeedbackVectorIndex());
// Compute flags for the call. // Compute flags for the call.
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
// Patch {node} to an indirect call via the CallFunction builtin. // Patch {node} to an indirect call via the CallFunction builtin.
......
...@@ -98,8 +98,8 @@ bool MapInference::Is(Handle<Map> expected_map) { ...@@ -98,8 +98,8 @@ bool MapInference::Is(Handle<Map> expected_map) {
return maps[0].equals(expected_map); return maps[0].equals(expected_map);
} }
void MapInference::InsertMapChecks(JSGraph* jsgraph, Node** effect, void MapInference::InsertMapChecks(JSGraph* jsgraph, Effect* effect,
Node* control, Control control,
const FeedbackSource& feedback) { const FeedbackSource& feedback) {
CHECK(HaveMaps()); CHECK(HaveMaps());
CHECK(feedback.IsValid()); CHECK(feedback.IsValid());
...@@ -114,7 +114,7 @@ void MapInference::InsertMapChecks(JSGraph* jsgraph, Node** effect, ...@@ -114,7 +114,7 @@ void MapInference::InsertMapChecks(JSGraph* jsgraph, Node** effect,
bool MapInference::RelyOnMapsViaStability( bool MapInference::RelyOnMapsViaStability(
CompilationDependencies* dependencies) { CompilationDependencies* dependencies) {
CHECK(HaveMaps()); CHECK(HaveMaps());
return RelyOnMapsHelper(dependencies, nullptr, nullptr, nullptr, {}); return RelyOnMapsHelper(dependencies, nullptr, nullptr, Control{nullptr}, {});
} }
bool MapInference::RelyOnMapsPreferStability( bool MapInference::RelyOnMapsPreferStability(
...@@ -123,13 +123,16 @@ bool MapInference::RelyOnMapsPreferStability( ...@@ -123,13 +123,16 @@ bool MapInference::RelyOnMapsPreferStability(
CHECK(HaveMaps()); CHECK(HaveMaps());
if (Safe()) return false; if (Safe()) return false;
if (RelyOnMapsViaStability(dependencies)) return true; if (RelyOnMapsViaStability(dependencies)) return true;
CHECK(RelyOnMapsHelper(nullptr, jsgraph, effect, control, feedback)); // TODO(jgruber): Change this to take (typed) Effect and Control parameters.
Effect e{*effect};
CHECK(RelyOnMapsHelper(nullptr, jsgraph, &e, Control{control}, feedback));
*effect = e;
return false; return false;
} }
bool MapInference::RelyOnMapsHelper(CompilationDependencies* dependencies, bool MapInference::RelyOnMapsHelper(CompilationDependencies* dependencies,
JSGraph* jsgraph, Node** effect, JSGraph* jsgraph, Effect* effect,
Node* control, Control control,
const FeedbackSource& feedback) { const FeedbackSource& feedback) {
if (Safe()) return true; if (Safe()) return true;
......
...@@ -69,8 +69,20 @@ class MapInference { ...@@ -69,8 +69,20 @@ class MapInference {
bool RelyOnMapsPreferStability(CompilationDependencies* dependencies, bool RelyOnMapsPreferStability(CompilationDependencies* dependencies,
JSGraph* jsgraph, Node** effect, Node* control, JSGraph* jsgraph, Node** effect, Node* control,
const FeedbackSource& feedback); const FeedbackSource& feedback);
// TODO(jgruber): Once all callsites pass Effect/Control types,
// remove the untyped version above.
bool RelyOnMapsPreferStability(CompilationDependencies* dependencies,
JSGraph* jsgraph, Effect* effect,
Control control,
const FeedbackSource& feedback) {
Node* effect_node = *effect;
bool result = RelyOnMapsPreferStability(dependencies, jsgraph, &effect_node,
control, feedback);
*effect = effect_node;
return result;
}
// Inserts map checks even if maps were already reliable. // Inserts map checks even if maps were already reliable.
void InsertMapChecks(JSGraph* jsgraph, Node** effect, Node* control, void InsertMapChecks(JSGraph* jsgraph, Effect* effect, Control control,
const FeedbackSource& feedback); const FeedbackSource& feedback);
// Internally marks the maps as reliable (thus bypassing the safety check) and // Internally marks the maps as reliable (thus bypassing the safety check) and
...@@ -98,8 +110,8 @@ class MapInference { ...@@ -98,8 +110,8 @@ class MapInference {
V8_WARN_UNUSED_RESULT bool AnyOfInstanceTypesUnsafe( V8_WARN_UNUSED_RESULT bool AnyOfInstanceTypesUnsafe(
std::function<bool(InstanceType)> f) const; std::function<bool(InstanceType)> f) const;
V8_WARN_UNUSED_RESULT bool RelyOnMapsHelper( V8_WARN_UNUSED_RESULT bool RelyOnMapsHelper(
CompilationDependencies* dependencies, JSGraph* jsgraph, Node** effect, CompilationDependencies* dependencies, JSGraph* jsgraph, Effect* effect,
Node* control, const FeedbackSource& feedback); Control control, const FeedbackSource& feedback);
}; };
} // namespace compiler } // namespace compiler
......
...@@ -258,6 +258,19 @@ void Node::TrimInputCount(int new_input_count) { ...@@ -258,6 +258,19 @@ void Node::TrimInputCount(int new_input_count) {
} }
} }
void Node::EnsureInputCount(Zone* zone, int new_input_count) {
int current_count = InputCount();
DCHECK_NE(current_count, 0);
if (current_count > new_input_count) {
TrimInputCount(new_input_count);
} else if (current_count < new_input_count) {
Node* dummy = InputAt(current_count - 1);
do {
AppendInput(zone, dummy);
current_count++;
} while (current_count < new_input_count);
}
}
int Node::UseCount() const { int Node::UseCount() const {
int use_count = 0; int use_count = 0;
......
...@@ -92,6 +92,8 @@ class V8_EXPORT_PRIVATE Node final { ...@@ -92,6 +92,8 @@ class V8_EXPORT_PRIVATE Node final {
void RemoveInput(int index); void RemoveInput(int index);
void NullAllInputs(); void NullAllInputs();
void TrimInputCount(int new_input_count); void TrimInputCount(int new_input_count);
// Can trim, extend by appending new inputs, or do nothing.
void EnsureInputCount(Zone* zone, int new_input_count);
int UseCount() const; int UseCount() const;
void ReplaceUses(Node* replace_to); void ReplaceUses(Node* replace_to);
......
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