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 {
void CheckIfElementsKind(Node* receiver_elements_kind, ElementsKind kind,
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;
......
......@@ -935,10 +935,14 @@ void JSGenericLowering::LowerJSCallForwardVarargs(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();
ConvertReceiverMode const mode = p.convert_mode();
Node* feedback_vector = n.feedback_vector();
node->RemoveInput(n.FeedbackVectorIndex());
if (CollectFeedbackInGenericLowering() && p.feedback().IsValid()) {
Callable callable = CodeFactory::Call_WithFeedback(isolate(), mode);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
......@@ -946,7 +950,6 @@ void JSGenericLowering::LowerJSCall(Node* node) {
zone(), callable.descriptor(), arg_count + 1, flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(arg_count);
Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector);
Node* slot = jsgraph()->Int32Constant(p.feedback().index());
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, stub_arity);
......
......@@ -50,36 +50,44 @@ class JSCallAccessor {
call->opcode() == IrOpcode::kJSConstruct);
}
Node* target() {
Node* target() const {
// Both, {JSCall} and {JSConstruct}, have same layout here.
return call_->InputAt(0);
}
Node* receiver() {
Node* receiver() const {
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());
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.
return NodeProperties::GetFrameStateInput(call_);
}
int formal_arguments() {
// Both, {JSCall} and {JSConstruct}, have two extra inputs:
// - JSConstruct: Includes target function and new target.
// - JSCall: Includes target function and receiver.
return call_->op()->ValueInputCount() - 2;
int formal_arguments() const {
// TODO(jgruber): Remove once the JSConstructNode wrapper is
// implemented.
return (call_->opcode() == IrOpcode::kJSCall)
? JSCallNode{call_}.ArgumentCount()
: call_->op()->ValueInputCount() - kTargetAndNewTarget;
}
CallFrequency const& frequency() const {
return (call_->opcode() == IrOpcode::kJSCall)
? CallParametersOf(call_->op()).frequency()
? JSCallNode{call_}.Parameters().frequency()
: ConstructParametersOf(call_->op()).frequency();
}
......@@ -106,7 +114,13 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
// {inliner_inputs} counts JSFunction, receiver, arguments, but not
// 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.
for (Edge edge : start->use_edges()) {
Node* use = edge.from();
......@@ -123,6 +137,9 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
Replace(use, new_target);
} else if (index == inlinee_arity_index) {
// 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));
} else if (index == inlinee_context_index) {
// The projection is requesting the inlinee function context.
......@@ -240,31 +257,36 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
FrameStateType frame_state_type,
SharedFunctionInfoRef shared,
Node* context) {
const int parameter_count_with_receiver =
parameter_count + JSCallNode::kReceiverInputCount;
const FrameStateFunctionInfo* state_info =
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(
bailout_id, OutputFrameStateCombine::Ignore(), state_info);
const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
Node* node0 = graph()->NewNode(op0);
static constexpr int kTargetInputIndex = 0;
static constexpr int kReceiverInputIndex = 1;
const int parameter_count_with_receiver = parameter_count + 1;
// Note how we refer to 'receiver' even though `node` may be a
// JSConstruct node. That's because the layout of construct nodes
// 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_);
for (int i = 0; i < parameter_count_with_receiver; i++) {
params.push_back(node->InputAt(kReceiverInputIndex + i));
params.push_back(node->InputAt(JSCallNode::ReceiverIndex()));
for (int i = 0; i < parameter_count; i++) {
params.push_back(call.argument(i));
}
const Operator* op_param = common()->StateValues(
static_cast<int>(params.size()), SparseInputMask::Dense());
Node* params_node = graph()->NewNode(
op_param, static_cast<int>(params.size()), &params.front());
if (!context) {
context = jsgraph()->UndefinedConstant();
}
return graph()->NewNode(op, params_node, node0, node0, context,
node->InputAt(kTargetInputIndex), outer_frame_state);
if (context == nullptr) context = jsgraph()->UndefinedConstant();
return graph()->NewNode(op, params_node, node0, node0, context, call.target(),
outer_frame_state);
}
namespace {
......@@ -283,7 +305,7 @@ bool NeedsImplicitReceiver(SharedFunctionInfoRef shared_info) {
base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget(
Node* node) {
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
// calls whenever the target is a constant function object, as follows:
......@@ -336,7 +358,7 @@ base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget(
FeedbackVectorRef JSInliner::DetermineCallContext(Node* node,
Node** context_out) {
DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
HeapObjectMatcher match(node->InputAt(0));
HeapObjectMatcher match(node->InputAt(JSCallNode::TargetIndex()));
if (match.HasValue() && match.Ref(broker()).IsJSFunction()) {
JSFunctionRef function = match.Ref(broker()).AsJSFunction();
......@@ -507,8 +529,10 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
// normal {JSCall} node so that the rest of the inlining machinery
// behaves as if we were dealing with a regular function invocation.
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->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
// constructor dispatch (allocate implicit receiver and check return value).
......@@ -575,7 +599,7 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
// Fix input destroyed by the above {ReplaceWithValue} call.
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
// reconstruct the proper frame when deoptimizing within the constructor.
frame_state = CreateArtificialFrameState(
......@@ -596,7 +620,8 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
Node* receiver = effect =
graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
call.receiver(), global_proxy, effect, start);
NodeProperties::ReplaceValueInput(node, receiver, 1);
NodeProperties::ReplaceValueInput(node, receiver,
JSCallNode::ReceiverIndex());
NodeProperties::ReplaceEffectInput(node, effect);
}
}
......
......@@ -330,8 +330,14 @@ Reduction JSIntrinsicLowering::ReduceToString(Node* node) {
Reduction JSIntrinsicLowering::ReduceCall(Node* node) {
size_t const arity = CallRuntimeParametersOf(node->op()).arity();
NodeProperties::ChangeOp(node, javascript()->Call(arity));
int const 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);
}
......
......@@ -482,13 +482,20 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// Call the @@hasInstance handler.
Node* target = jsgraph()->Constant(*constant);
node->InsertInput(graph()->zone(), 0, target);
node->ReplaceInput(1, constructor);
node->ReplaceInput(2, object);
node->ReplaceInput(4, continuation_frame_state);
node->ReplaceInput(5, effect);
Node* feedback = jsgraph()->UndefinedConstant();
// Value inputs plus context, frame state, effect, control.
STATIC_ASSERT(JSCallNode::ArityForArgc(1) + 4 == 8);
node->EnsureInputCount(graph()->zone(), 8);
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(
node, javascript()->Call(1 + kTargetAndReceiver, CallFrequency(),
node, javascript()->Call(JSCallNode::ArityForArgc(1), CallFrequency(),
FeedbackSource(),
ConvertReceiverMode::kNotNullOrUndefined));
......@@ -1430,11 +1437,12 @@ Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
? SpeculationMode::kDisallowSpeculation
: feedback.AsCall().speculation_mode();
const Operator* call_op = javascript()->Call(
0 + kTargetAndReceiver, CallFrequency(), p.callFeedback(),
JSCallNode::ArityForArgc(0), CallFrequency(), p.callFeedback(),
ConvertReceiverMode::kNotNullOrUndefined, mode,
CallFeedbackRelation::kRelated);
Node* call_property = graph()->NewNode(call_op, load_property, receiver,
context, frame_state, effect, control);
Node* call_property =
graph()->NewNode(call_op, load_property, receiver, n.feedback_vector(),
context, frame_state, effect, control);
return Replace(call_property);
}
......@@ -2043,11 +2051,12 @@ Node* JSNativeContextSpecialization::InlinePropertyGetterCall(
// Introduce the call to the getter function.
Node* value;
if (constant.IsJSFunction()) {
Node* feedback = jsgraph()->UndefinedConstant();
value = *effect = *control = graph()->NewNode(
jsgraph()->javascript()->Call(0 + kTargetAndReceiver, CallFrequency(),
FeedbackSource(),
jsgraph()->javascript()->Call(JSCallNode::ArityForArgc(0),
CallFrequency(), FeedbackSource(),
ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, context, frame_state, *effect, *control);
target, receiver, feedback, context, frame_state, *effect, *control);
} else {
Node* holder = access_info.holder().is_null()
? receiver
......@@ -2081,11 +2090,13 @@ void JSNativeContextSpecialization::InlinePropertySetterCall(
FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op());
// Introduce the call to the setter function.
if (constant.IsJSFunction()) {
Node* feedback = jsgraph()->UndefinedConstant();
*effect = *control = graph()->NewNode(
jsgraph()->javascript()->Call(1 + kTargetAndReceiver, CallFrequency(),
FeedbackSource(),
jsgraph()->javascript()->Call(JSCallNode::ArityForArgc(1),
CallFrequency(), FeedbackSource(),
ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, value, context, frame_state, *effect, *control);
target, receiver, value, feedback, context, frame_state, *effect,
*control);
} else {
Node* holder = access_info.holder().is_null()
? receiver
......
......@@ -7,6 +7,7 @@
#include <limits>
#include "src/base/lazy-instance.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/operator.h"
#include "src/handles/handles-inl.h"
#include "src/objects/objects-inl.h"
......@@ -27,6 +28,10 @@ constexpr Operator::Properties BinopProperties(Operator::Opcode opcode) {
} // namespace
TNode<Object> JSCallNode::ArgumentOrUndefined(int i, JSGraph* jsgraph) const {
return ArgumentOr(i, jsgraph->UndefinedConstant());
}
std::ostream& operator<<(std::ostream& os, CallFrequency const& f) {
if (f.IsUnknown()) return os << "unknown";
return os << f.value();
......@@ -768,7 +773,7 @@ const Operator* JSOperatorBuilder::Call(
FeedbackSource const& feedback, ConvertReceiverMode convert_mode,
SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) {
CallParameters parameters(arity, frequency, feedback, convert_mode,
speculation_mode, feedback_relation);
speculation_mode, feedback_relation, true);
return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCall, Operator::kNoProperties, // opcode
"JSCall", // name
......@@ -780,7 +785,7 @@ const Operator* JSOperatorBuilder::CallWithArrayLike(
const CallFrequency& frequency, const FeedbackSource& feedback,
SpeculationMode speculation_mode, CallFeedbackRelation feedback_relation) {
CallParameters parameters(2, frequency, feedback, ConvertReceiverMode::kAny,
speculation_mode, feedback_relation);
speculation_mode, feedback_relation, false);
return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCallWithArrayLike, Operator::kNoProperties, // opcode
"JSCallWithArrayLike", // name
......@@ -796,7 +801,7 @@ const Operator* JSOperatorBuilder::CallWithSpread(
feedback.IsValid());
CallParameters parameters(arity, frequency, feedback,
ConvertReceiverMode::kAny, speculation_mode,
feedback_relation);
feedback_relation, false);
return new (zone()) Operator1<CallParameters>( // --
IrOpcode::kJSCallWithSpread, Operator::kNoProperties, // opcode
"JSCallWithSpread", // name
......
......@@ -28,6 +28,7 @@ class SharedFunctionInfo;
namespace compiler {
// Forward declarations.
class JSGraph;
class Operator;
struct JSOperatorGlobalCache;
......@@ -218,7 +219,9 @@ CallForwardVarargsParameters const& CallForwardVarargsParametersOf(
Operator const*) V8_WARN_UNUSED_RESULT;
// Part of CallParameters::arity.
// TODO(jgruber): Fold this into JSCallNode.
static constexpr int kTargetAndReceiver = 2;
static constexpr int kTargetAndReceiverAndVector = 3;
// 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,
......@@ -229,19 +232,21 @@ class CallParameters final {
FeedbackSource const& feedback,
ConvertReceiverMode convert_mode,
SpeculationMode speculation_mode,
CallFeedbackRelation feedback_relation)
CallFeedbackRelation feedback_relation, bool has_vector)
: bit_field_(ArityField::encode(arity) |
CallFeedbackRelationField::encode(feedback_relation) |
SpeculationModeField::encode(speculation_mode) |
ConvertReceiverModeField::encode(convert_mode)),
frequency_(frequency),
feedback_(feedback) {
feedback_(feedback),
has_vector_(has_vector) {
// 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);
DCHECK_GE(arity, kTargetAndReceiver);
DCHECK_GE(arity,
has_vector_ ? kTargetAndReceiverAndVector : kTargetAndReceiver);
DCHECK(is_int32(arity));
}
......@@ -249,7 +254,8 @@ class CallParameters final {
// without extra args in CallParameters.
size_t arity() const { return ArityField::decode(bit_field_); }
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_; }
......@@ -288,6 +294,8 @@ class CallParameters final {
uint32_t const bit_field_;
CallFrequency const frequency_;
FeedbackSource const feedback_;
// TODO(jgruber): Remove once Call variants all have a feedback vector input.
bool has_vector_;
};
size_t hash_value(CallParameters const&);
......@@ -1214,6 +1222,61 @@ class JSStorePropertyNode final : public JSNodeWrapperBase {
#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
} // namespace compiler
......
......@@ -1710,16 +1710,16 @@ Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) {
}
Reduction JSTypedLowering::ReduceJSCall(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
JSCallNode n(node);
CallParameters const& p = n.Parameters();
int arity = p.arity_without_implicit_args();
ConvertReceiverMode convert_mode = p.convert_mode();
Node* target = NodeProperties::GetValueInput(node, 0);
Node* target = n.target();
Type target_type = NodeProperties::GetType(target);
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* receiver = n.receiver();
Type receiver_type = NodeProperties::GetType(receiver);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Effect effect = n.effect();
Control control = n.control();
// Try to infer receiver {convert_mode} from {receiver} type.
if (receiver_type.Is(Type::NullOrUndefined())) {
......@@ -1787,6 +1787,9 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
Node* new_target = jsgraph()->UndefinedConstant();
// The node will change operators, remove the feedback vector.
node->RemoveInput(n.FeedbackVectorIndex());
if (NeedsArgumentAdaptorFrame(*shared, arity)) {
// Check if it's safe to skip the arguments adaptor for {shared},
// that is whether the target function anyways cannot observe the
......@@ -1865,6 +1868,8 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
// Check if {target} is a JSFunction.
if (target_type.Is(Type::Function())) {
// The node will change operators, remove the feedback vector.
node->RemoveInput(n.FeedbackVectorIndex());
// Compute flags for the call.
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
// Patch {node} to an indirect call via the CallFunction builtin.
......
......@@ -98,8 +98,8 @@ bool MapInference::Is(Handle<Map> expected_map) {
return maps[0].equals(expected_map);
}
void MapInference::InsertMapChecks(JSGraph* jsgraph, Node** effect,
Node* control,
void MapInference::InsertMapChecks(JSGraph* jsgraph, Effect* effect,
Control control,
const FeedbackSource& feedback) {
CHECK(HaveMaps());
CHECK(feedback.IsValid());
......@@ -114,7 +114,7 @@ void MapInference::InsertMapChecks(JSGraph* jsgraph, Node** effect,
bool MapInference::RelyOnMapsViaStability(
CompilationDependencies* dependencies) {
CHECK(HaveMaps());
return RelyOnMapsHelper(dependencies, nullptr, nullptr, nullptr, {});
return RelyOnMapsHelper(dependencies, nullptr, nullptr, Control{nullptr}, {});
}
bool MapInference::RelyOnMapsPreferStability(
......@@ -123,13 +123,16 @@ bool MapInference::RelyOnMapsPreferStability(
CHECK(HaveMaps());
if (Safe()) return false;
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;
}
bool MapInference::RelyOnMapsHelper(CompilationDependencies* dependencies,
JSGraph* jsgraph, Node** effect,
Node* control,
JSGraph* jsgraph, Effect* effect,
Control control,
const FeedbackSource& feedback) {
if (Safe()) return true;
......
......@@ -69,8 +69,20 @@ class MapInference {
bool RelyOnMapsPreferStability(CompilationDependencies* dependencies,
JSGraph* jsgraph, Node** effect, Node* control,
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.
void InsertMapChecks(JSGraph* jsgraph, Node** effect, Node* control,
void InsertMapChecks(JSGraph* jsgraph, Effect* effect, Control control,
const FeedbackSource& feedback);
// Internally marks the maps as reliable (thus bypassing the safety check) and
......@@ -98,8 +110,8 @@ class MapInference {
V8_WARN_UNUSED_RESULT bool AnyOfInstanceTypesUnsafe(
std::function<bool(InstanceType)> f) const;
V8_WARN_UNUSED_RESULT bool RelyOnMapsHelper(
CompilationDependencies* dependencies, JSGraph* jsgraph, Node** effect,
Node* control, const FeedbackSource& feedback);
CompilationDependencies* dependencies, JSGraph* jsgraph, Effect* effect,
Control control, const FeedbackSource& feedback);
};
} // namespace compiler
......
......@@ -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 use_count = 0;
......
......@@ -92,6 +92,8 @@ class V8_EXPORT_PRIVATE Node final {
void RemoveInput(int index);
void NullAllInputs();
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;
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