Commit b8fbf8eb authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[nci] Add node wrappers for construct variants

... in preparation for upcoming changes to 1. make construct node
layout more consistent with call nodes by placing new_target
(construct) in the same spot as receiver (call); and 2. adding the
feedback vector input.

Bug: v8:8888
Change-Id: I6cd7f50ed0b029de53af5cd82e7ecf4ba514ef65
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2275963
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68689}
parent 8c0b68e3
......@@ -2582,15 +2582,26 @@ void BytecodeGraphBuilder::VisitCallRuntimeForPair() {
Node* const* BytecodeGraphBuilder::GetConstructArgumentsFromRegister(
Node* target, Node* new_target, interpreter::Register first_arg,
int arg_count) {
int arity = kTargetAndNewTarget + arg_count;
const int arity = JSConstructNode::ArityForArgc(arg_count);
Node** all = local_zone()->NewArray<Node*>(static_cast<size_t>(arity));
all[0] = target;
int first_arg_index = first_arg.index();
int cursor = 0;
STATIC_ASSERT(JSConstructNode::TargetIndex() == 0);
STATIC_ASSERT(JSConstructNode::FirstArgumentIndex() == 1);
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
all[cursor++] = target;
// The function arguments are in consecutive registers.
int arg_base = first_arg.index();
for (int i = 0; i < arg_count; ++i) {
all[1 + i] = environment()->LookupRegister(
interpreter::Register(first_arg_index + i));
all[cursor++] =
environment()->LookupRegister(interpreter::Register(arg_base + i));
}
all[arity - 1] = new_target;
all[cursor++] = new_target;
DCHECK_EQ(cursor, arity);
return all;
}
......@@ -2607,9 +2618,8 @@ void BytecodeGraphBuilder::VisitConstruct() {
CallFrequency frequency = ComputeCallFrequency(slot_id);
const uint32_t arg_count = static_cast<uint32_t>(reg_count);
const uint32_t arg_count_with_extra_args = kTargetAndNewTarget + arg_count;
const Operator* op =
javascript()->Construct(arg_count_with_extra_args, frequency, feedback);
const uint32_t arity = JSConstructNode::ArityForArgc(arg_count);
const Operator* op = javascript()->Construct(arity, frequency, feedback);
Node* const* args = GetConstructArgumentsFromRegister(callee, new_target,
first_reg, arg_count);
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedConstruct(
......@@ -2621,7 +2631,7 @@ void BytecodeGraphBuilder::VisitConstruct() {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = MakeNode(op, arg_count_with_extra_args, args);
node = MakeNode(op, arity, args);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
......@@ -2639,9 +2649,9 @@ void BytecodeGraphBuilder::VisitConstructWithSpread() {
CallFrequency frequency = ComputeCallFrequency(slot_id);
const uint32_t arg_count = static_cast<uint32_t>(reg_count);
const uint32_t arg_count_with_extra_args = kTargetAndNewTarget + arg_count;
const Operator* op = javascript()->ConstructWithSpread(
arg_count_with_extra_args, frequency, feedback);
const uint32_t arity = JSConstructNode::ArityForArgc(arg_count);
const Operator* op =
javascript()->ConstructWithSpread(arity, frequency, feedback);
Node* const* args = GetConstructArgumentsFromRegister(callee, new_target,
first_reg, arg_count);
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedConstruct(
......@@ -2653,7 +2663,7 @@ void BytecodeGraphBuilder::VisitConstructWithSpread() {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = MakeNode(op, arg_count_with_extra_args, args);
node = MakeNode(op, arity, args);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
......
......@@ -106,7 +106,7 @@ uint8_t DeoptimizerParameterCountFor(ContinuationFrameStateMode mode) {
UNREACHABLE();
}
Node* CreateBuiltinContinuationFrameStateCommon(
FrameState CreateBuiltinContinuationFrameStateCommon(
JSGraph* jsgraph, FrameStateType frame_type, Builtins::Name name,
Node* closure, Node* context, Node** parameters, int parameter_count,
Node* outer_frame_state,
......@@ -124,14 +124,14 @@ Node* CreateBuiltinContinuationFrameStateCommon(
shared);
const Operator* op = common->FrameState(
bailout_id, OutputFrameStateCombine::Ignore(), state_info);
return graph->NewNode(op, params_node, jsgraph->EmptyStateValues(),
jsgraph->EmptyStateValues(), context, closure,
outer_frame_state);
return FrameState(graph->NewNode(op, params_node, jsgraph->EmptyStateValues(),
jsgraph->EmptyStateValues(), context,
closure, outer_frame_state));
}
} // namespace
Node* CreateStubBuiltinContinuationFrameState(
FrameState CreateStubBuiltinContinuationFrameState(
JSGraph* jsgraph, Builtins::Name name, Node* context,
Node* const* parameters, int parameter_count, Node* outer_frame_state,
ContinuationFrameStateMode mode) {
......@@ -169,7 +169,7 @@ Node* CreateStubBuiltinContinuationFrameState(
static_cast<int>(actual_parameters.size()), outer_frame_state);
}
Node* CreateJavaScriptBuiltinContinuationFrameState(
FrameState CreateJavaScriptBuiltinContinuationFrameState(
JSGraph* jsgraph, const SharedFunctionInfoRef& shared, Builtins::Name name,
Node* target, Node* context, Node* const* stack_parameters,
int stack_parameter_count, Node* outer_frame_state,
......@@ -207,7 +207,7 @@ Node* CreateJavaScriptBuiltinContinuationFrameState(
shared.object());
}
Node* CreateGenericLazyDeoptContinuationFrameState(
FrameState CreateGenericLazyDeoptContinuationFrameState(
JSGraph* graph, const SharedFunctionInfoRef& shared, Node* target,
Node* context, Node* receiver, Node* outer_frame_state) {
Node* stack_parameters[]{receiver};
......
......@@ -6,6 +6,7 @@
#define V8_COMPILER_FRAME_STATES_H_
#include "src/builtins/builtins.h"
#include "src/compiler/node.h"
#include "src/handles/handles.h"
#include "src/objects/shared-function-info.h"
#include "src/utils/utils.h"
......@@ -150,18 +151,18 @@ static constexpr int kFrameStateInputCount = kFrameStateOuterStateInput + 1;
enum class ContinuationFrameStateMode { EAGER, LAZY, LAZY_WITH_CATCH };
Node* CreateStubBuiltinContinuationFrameState(
FrameState CreateStubBuiltinContinuationFrameState(
JSGraph* graph, Builtins::Name name, Node* context, Node* const* parameters,
int parameter_count, Node* outer_frame_state,
ContinuationFrameStateMode mode);
Node* CreateJavaScriptBuiltinContinuationFrameState(
FrameState CreateJavaScriptBuiltinContinuationFrameState(
JSGraph* graph, const SharedFunctionInfoRef& shared, Builtins::Name name,
Node* target, Node* context, Node* const* stack_parameters,
int stack_parameter_count, Node* outer_frame_state,
ContinuationFrameStateMode mode);
Node* CreateGenericLazyDeoptContinuationFrameState(
FrameState CreateGenericLazyDeoptContinuationFrameState(
JSGraph* graph, const SharedFunctionInfoRef& shared, Node* target,
Node* context, Node* receiver, Node* outer_frame_state);
......
This diff is collapsed.
......@@ -760,13 +760,11 @@ void JSGenericLowering::LowerJSConstructForwardVarargs(Node* node) {
}
void JSGenericLowering::LowerJSConstruct(Node* node) {
ConstructParameters const& p = ConstructParametersOf(node->op());
JSConstructNode n(node);
ConstructParameters const& p = n.Parameters();
int const arg_count = p.arity_without_implicit_args();
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
// TODO(jgruber): Understand and document how stack_argument_count is
// calculated. I've made some educated guesses below but they should be
// verified and documented in other lowerings as well.
static constexpr int kReceiver = 1;
static constexpr int kMaybeFeedbackVector = 1;
......@@ -778,13 +776,14 @@ void JSGenericLowering::LowerJSConstruct(Node* node) {
Builtins::CallableFor(isolate(), Builtins::kConstruct_WithFeedback);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* new_target = node->InputAt(arg_count + 1);
Node* new_target = n.new_target();
Node* stub_arity = jsgraph()->Int32Constant(arg_count);
Node* slot = jsgraph()->Int32Constant(p.feedback().index());
Node* receiver = jsgraph()->UndefinedConstant();
node->RemoveInput(arg_count + 1); // Drop new target.
node->RemoveInput(n.NewTargetIndex());
// Register argument inputs are followed by stack argument inputs (such as
// feedback_vector). Both are listed in ascending order. Note that
// the receiver is implicitly placed on the stack and is thus inserted
......@@ -796,6 +795,10 @@ void JSGenericLowering::LowerJSConstruct(Node* node) {
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, slot);
node->InsertInput(zone(), 5, receiver);
// After: {code, target, new_target, arity, slot, receiver, ...args,
// vector}.
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
} else {
const int stack_argument_count = arg_count + kReceiver;
......@@ -804,19 +807,23 @@ void JSGenericLowering::LowerJSConstruct(Node* node) {
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(arg_count);
Node* new_target = node->InputAt(arg_count + 1);
Node* new_target = n.new_target();
Node* receiver = jsgraph()->UndefinedConstant();
node->RemoveInput(arg_count + 1); // Drop new target.
node->RemoveInput(n.NewTargetIndex());
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, new_target);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, receiver);
// After: {code, target, new_target, arity, receiver, ...args}.
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
}
void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) {
ConstructParameters const& p = ConstructParametersOf(node->op());
JSConstructWithArrayLikeNode n(node);
ConstructParameters const& p = n.Parameters();
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
const int arg_count = p.arity_without_implicit_args();
DCHECK_EQ(arg_count, 1);
......@@ -835,8 +842,9 @@ void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) {
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* receiver = jsgraph()->UndefinedConstant();
Node* arguments_list = node->InputAt(1);
Node* new_target = node->InputAt(2);
Node* arguments_list = n.Argument(0);
Node* new_target = n.new_target();
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector);
Node* slot = jsgraph()->Int32Constant(p.feedback().index());
......@@ -852,6 +860,9 @@ void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) {
node->InsertInput(zone(), 5, receiver);
node->InsertInput(zone(), 6, feedback_vector);
// After: {code, target, new_target, arguments_list, slot, receiver,
// vector}.
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
} else {
const int stack_argument_count = arg_count - kArgumentList + kReceiver;
......@@ -861,21 +872,24 @@ void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) {
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* receiver = jsgraph()->UndefinedConstant();
Node* arguments_list = node->InputAt(1);
Node* new_target = node->InputAt(2);
Node* arguments_list = n.Argument(0);
Node* new_target = n.new_target();
node->InsertInput(zone(), 0, stub_code);
node->ReplaceInput(2, new_target);
node->ReplaceInput(3, arguments_list);
node->InsertInput(zone(), 4, receiver);
// After: {code, target, new_target, arguments_list, receiver}.
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
}
void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
ConstructParameters const& p = ConstructParametersOf(node->op());
JSConstructWithSpreadNode n(node);
ConstructParameters const& p = n.Parameters();
int const arg_count = p.arity_without_implicit_args();
int const spread_index = arg_count;
int const new_target_index = arg_count + 1;
DCHECK_GE(arg_count, 1);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
static constexpr int kReceiver = 1;
......@@ -891,15 +905,15 @@ void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector);
Node* slot = jsgraph()->Int32Constant(p.feedback().index());
// The single available register is needed for `slot`, thus `spread` remains
// on the stack here.
Node* stub_arity = jsgraph()->Int32Constant(arg_count - kTheSpread);
Node* new_target = node->InputAt(new_target_index);
Node* new_target = node->RemoveInput(n.NewTargetIndex());
Node* receiver = jsgraph()->UndefinedConstant();
node->RemoveInput(new_target_index);
// Register argument inputs are followed by stack argument inputs (such as
// feedback_vector). Both are listed in ascending order. Note that
......@@ -912,6 +926,10 @@ void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, slot);
node->InsertInput(zone(), 5, receiver);
// After: {code, target, new_target, arity, slot, receiver, ...args, spread,
// vector}.
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
} else {
const int stack_argument_count = arg_count + kReceiver - kTheSpread;
......@@ -922,18 +940,20 @@ void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
// We pass the spread in a register, not on the stack.
Node* stub_arity = jsgraph()->Int32Constant(arg_count - kTheSpread);
Node* new_target = node->InputAt(new_target_index);
Node* spread = node->InputAt(spread_index);
Node* receiver = jsgraph()->UndefinedConstant();
DCHECK(new_target_index > spread_index);
node->RemoveInput(new_target_index);
node->RemoveInput(spread_index);
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
DCHECK(n.NewTargetIndex() > n.LastArgumentIndex());
Node* new_target = node->RemoveInput(n.NewTargetIndex());
Node* spread = node->RemoveInput(n.LastArgumentIndex());
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, new_target);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, spread);
node->InsertInput(zone(), 5, receiver);
// After: {code, target, new_target, arity, spread, receiver, ...args}.
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
}
......@@ -998,12 +1018,12 @@ void JSGenericLowering::LowerJSCallWithArrayLike(Node* node) {
DCHECK_EQ(arg_count, 1); // The arraylike object.
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
static constexpr int kArrayLikeObject = 1;
static constexpr int kArgumentsList = 1;
static constexpr int kReceiver = 1;
if (CollectFeedbackInGenericLowering() &&
CollectCallAndConstructFeedback(broker()) && p.feedback().IsValid()) {
const int stack_argument_count = arg_count - kArrayLikeObject + kReceiver;
const int stack_argument_count = arg_count - kArgumentsList + kReceiver;
Callable callable = Builtins::CallableFor(
isolate(), Builtins::kCallWithArrayLike_WithFeedback);
auto call_descriptor = Linkage::GetStubCallDescriptor(
......@@ -1030,7 +1050,7 @@ void JSGenericLowering::LowerJSCallWithArrayLike(Node* node) {
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
} else {
const int stack_argument_count = arg_count - kArrayLikeObject + kReceiver;
const int stack_argument_count = arg_count - kArgumentsList + kReceiver;
Callable callable = CodeFactory::CallWithArrayLike(isolate());
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
......
......@@ -654,7 +654,13 @@ void JSInliningHeuristic::CreateOrReuseDispatch(Node* node, Node* callee,
// We also specialize the new.target of JSConstruct {node}s if it refers
// to the same node as the {node}'s target input, so that we can later
// properly inline the JSCreate operations.
if (node->opcode() == IrOpcode::kJSConstruct) {
UNREACHABLE(); // https://crbug.com/v8/10675.
}
if (node->opcode() == IrOpcode::kJSConstruct && inputs[0] == inputs[1]) {
// TODO(jgruber): Is this correct? JSConstruct nodes have the new_target
// at the last index, not at index 1.
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
inputs[1] = target;
}
inputs[0] = target;
......
......@@ -51,44 +51,30 @@ class JSCallAccessor {
}
Node* target() const {
// Both, {JSCall} and {JSConstruct}, have same layout here.
return call_->InputAt(0);
STATIC_ASSERT(JSCallNode::TargetIndex() == JSConstructNode::TargetIndex());
return call_->InputAt(JSCallNode::TargetIndex());
}
Node* receiver() const {
DCHECK_EQ(IrOpcode::kJSCall, call_->opcode());
return JSCallNode{call_}.receiver();
}
Node* new_target() const {
DCHECK_EQ(IrOpcode::kJSConstruct, call_->opcode());
return call_->InputAt(formal_arguments() + 1);
}
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* new_target() const { return JSConstructNode{call_}.new_target(); }
Node* frame_state() const {
// Both, {JSCall} and {JSConstruct}, have frame state.
return NodeProperties::GetFrameStateInput(call_);
}
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;
: JSConstructNode{call_}.ArgumentCount();
}
CallFrequency const& frequency() const {
return (call_->opcode() == IrOpcode::kJSCall)
? JSCallNode{call_}.Parameters().frequency()
: ConstructParametersOf(call_->op()).frequency();
: JSConstructNode{call_}.Parameters().frequency();
}
private:
......@@ -115,7 +101,8 @@ 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.
// TODO(jgruber): Refactor this once JSConstructNode is implemented.
STATIC_ASSERT(kTargetAndNewTarget == 2);
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
STATIC_ASSERT(JSConstructNode::kExtraInputCount == 2);
const bool inliner_is_call = call->opcode() == IrOpcode::kJSCall;
const int inliner_inputs = inliner_is_call
? call->op()->ValueInputCount() -
......@@ -139,7 +126,7 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
// The projection is requesting the number of arguments.
STATIC_ASSERT(JSCallNode::kExtraInputCount ==
JSCallNode::kFeedbackVectorInputCount + 2);
STATIC_ASSERT(kTargetAndNewTarget == 2);
STATIC_ASSERT(JSConstructNode::kExtraInputCount == 2);
Replace(use, jsgraph()->Constant(inliner_inputs - 2));
} else if (index == inlinee_context_index) {
// The projection is requesting the inlinee function context.
......@@ -274,18 +261,19 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
// `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);
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
NodeVector params(local_zone_);
params.push_back(node->InputAt(JSCallNode::ReceiverIndex()));
for (int i = 0; i < parameter_count; i++) {
params.push_back(call.argument(i));
params.push_back(node->InputAt(JSCallNode::ArgumentIndex(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 == nullptr) context = jsgraph()->UndefinedConstant();
return graph()->NewNode(op, params_node, node0, node0, context, call.target(),
return graph()->NewNode(op, params_node, node0, node0, context,
node->InputAt(JSCallNode::TargetIndex()),
outer_frame_state);
}
......@@ -311,6 +299,7 @@ base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget(
// calls whenever the target is a constant function object, as follows:
// - JSCall(target:constant, receiver, args...)
// - JSConstruct(target:constant, args..., new.target)
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
if (match.HasValue() && match.Ref(broker()).IsJSFunction()) {
JSFunctionRef function = match.Ref(broker()).AsJSFunction();
......@@ -338,6 +327,7 @@ base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget(
// be the result of a closure instantiation operation, as follows:
// - JSCall(JSCreateClosure[shared](context), receiver, args...)
// - JSConstruct(JSCreateClosure[shared](context), args..., new.target)
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
if (match.IsJSCreateClosure()) {
CreateClosureParameters const& p = CreateClosureParametersOf(match.op());
FeedbackCellRef cell(broker(), p.feedback_cell());
......@@ -525,13 +515,12 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
// Inline {JSConstruct} requires some additional magic.
if (node->opcode() == IrOpcode::kJSConstruct) {
JSConstructNode n(node);
// Swizzle the inputs of the {JSConstruct} node to look like inputs to a
// 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.
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
new_target = node->RemoveInput(n.NewTargetIndex());
node->InsertInput(graph()->zone(), JSCallNode::ReceiverIndex(), new_target);
// Insert nodes around the call that model the behavior required for a
......@@ -545,10 +534,10 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
Node* receiver = jsgraph()->TheHoleConstant(); // Implicit receiver.
Node* context = NodeProperties::GetContextInput(node);
if (NeedsImplicitReceiver(*shared_info)) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Effect effect = n.effect();
Control control = n.control();
Node* frame_state_inside = CreateArtificialFrameState(
node, frame_state, call.formal_arguments(),
node, frame_state, n.ArgumentCount(),
BailoutId::ConstructStubCreate(), FrameStateType::kConstructStub,
*shared_info, context);
Node* create =
......@@ -603,9 +592,8 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
// 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(
node, frame_state, call.formal_arguments(),
BailoutId::ConstructStubInvoke(), FrameStateType::kConstructStub,
*shared_info, context);
node, frame_state, n.ArgumentCount(), BailoutId::ConstructStubInvoke(),
FrameStateType::kConstructStub, *shared_info, context);
}
// Insert a JSConvertReceiver node for sloppy callees. Note that the context
......
......@@ -28,15 +28,13 @@ constexpr Operator::Properties BinopProperties(Operator::Opcode opcode) {
} // namespace
template <int kOpcode>
TNode<Object> JSCallNodeBase<kOpcode>::ArgumentOrUndefined(
int i, JSGraph* jsgraph) const {
return ArgumentOr(i, jsgraph->UndefinedConstant());
namespace js_node_wrapper_utils {
TNode<Oddball> UndefinedConstant(JSGraph* jsgraph) {
return TNode<Oddball>::UncheckedCast(jsgraph->UndefinedConstant());
}
template class JSCallNodeBase<IrOpcode::kJSCall>;
template class JSCallNodeBase<IrOpcode::kJSCallWithSpread>;
template class JSCallNodeBase<IrOpcode::kJSCallWithArrayLike>;
} // namespace js_node_wrapper_utils
std::ostream& operator<<(std::ostream& os, CallFrequency const& f) {
if (f.IsUnknown()) return os << "unknown";
......
......@@ -140,18 +140,19 @@ std::ostream& operator<<(std::ostream&,
ConstructForwardVarargsParameters const& ConstructForwardVarargsParametersOf(
Operator const*) V8_WARN_UNUSED_RESULT;
// Part of ConstructParameters::arity.
static constexpr int kTargetAndNewTarget = 2;
// Defines the arity (parameters plus the target and new target) and the
// feedback for a JavaScript constructor call. This is used as a parameter by
// JSConstruct, JSConstructWithArrayLike, and JSConstructWithSpread operators.
class ConstructParameters final {
public:
// A separate declaration to get around circular declaration dependencies.
// Checked to equal JSConstructNode::kExtraInputCount below.
static constexpr int kExtraConstructInputCount = 2;
ConstructParameters(uint32_t arity, CallFrequency const& frequency,
FeedbackSource const& feedback)
: arity_(arity), frequency_(frequency), feedback_(feedback) {
DCHECK_GE(arity, kTargetAndNewTarget);
DCHECK_GE(arity, kExtraConstructInputCount);
DCHECK(is_int32(arity));
}
......@@ -163,7 +164,7 @@ class ConstructParameters final {
// that expect `arity()` to include extra args.
uint32_t arity() const { return arity_; }
int arity_without_implicit_args() const {
return static_cast<int>(arity_ - kTargetAndNewTarget);
return static_cast<int>(arity_ - kExtraConstructInputCount);
}
CallFrequency const& frequency() const { return frequency_; }
......@@ -1216,6 +1217,11 @@ class JSStorePropertyNode final : public JSNodeWrapperBase {
#undef INPUTS
};
namespace js_node_wrapper_utils {
// Avoids template definitions in the .cc file.
TNode<Oddball> UndefinedConstant(JSGraph* jsgraph);
} // namespace js_node_wrapper_utils
template <int kOpcode>
class JSCallNodeBase final : public JSNodeWrapperBase {
public:
......@@ -1271,7 +1277,9 @@ class JSCallNodeBase final : public JSNodeWrapperBase {
return i < ArgumentCount() ? Argument(i)
: TNode<Object>::UncheckedCast(default_value);
}
TNode<Object> ArgumentOrUndefined(int i, JSGraph* jsgraph) const;
TNode<Object> ArgumentOrUndefined(int i, JSGraph* jsgraph) const {
return ArgumentOr(i, js_node_wrapper_utils::UndefinedConstant(jsgraph));
}
int ArgumentCount() const {
// Note: The count reported by this function depends only on the parameter,
// thus adding/removing inputs will not affect it.
......@@ -1295,6 +1303,85 @@ using JSCallNode = JSCallNodeBase<IrOpcode::kJSCall>;
using JSCallWithSpreadNode = JSCallNodeBase<IrOpcode::kJSCallWithSpread>;
using JSCallWithArrayLikeNode = JSCallNodeBase<IrOpcode::kJSCallWithArrayLike>;
template <int kOpcode>
class JSConstructNodeBase final : public JSNodeWrapperBase {
public:
explicit constexpr JSConstructNodeBase(Node* node) : JSNodeWrapperBase(node) {
CONSTEXPR_DCHECK(node->opcode() == kOpcode);
}
const ConstructParameters& Parameters() const {
return ConstructParametersOf(node()->op());
}
#define INPUTS(V) V(Target, target, 0, Object)
INPUTS(DEFINE_INPUT_ACCESSORS)
#undef INPUTS
// Besides actual arguments, JSConstruct nodes (and variants) also take the
// following. Note that we rely on the fact that all variants (JSConstruct,
// JSConstructWithArrayLike, JSConstructWithSpread) have the same underlying
// node layout.
static constexpr int kTargetInputCount = 1;
static constexpr int kNewTargetInputCount = 1;
static constexpr int kExtraInputCount =
kTargetInputCount + kNewTargetInputCount;
STATIC_ASSERT(kExtraInputCount ==
ConstructParameters::kExtraConstructInputCount);
// Just for static asserts for spots that rely on node layout.
static constexpr bool kNewTargetIsLastInput = true;
// This is the arity fed into ConstructArguments.
static constexpr int ArityForArgc(int parameters) {
return parameters + kExtraInputCount;
}
static constexpr int FirstArgumentIndex() { return TargetIndex() + 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)));
}
int LastArgumentIndex() const {
DCHECK_GT(ArgumentCount(), 0);
return ArgumentIndex(ArgumentCount() - 1);
}
TNode<Object> LastArgument() const {
DCHECK_GT(ArgumentCount(), 0);
return Argument(ArgumentCount() - 1);
}
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 {
return ArgumentOr(i, js_node_wrapper_utils::UndefinedConstant(jsgraph));
}
int ArgumentCount() const {
// Note: The count reported by this function remains static even if the node
// is currently being modified.
return Parameters().arity_without_implicit_args();
}
static int NewTargetIndexForArgc(int argc) {
STATIC_ASSERT(kNewTargetIsLastInput);
return ArgumentIndex(argc - 1) + 1;
}
int NewTargetIndex() const { return NewTargetIndexForArgc(ArgumentCount()); }
TNode<Object> new_target() const {
return TNode<Object>::UncheckedCast(
NodeProperties::GetValueInput(node(), NewTargetIndex()));
}
};
using JSConstructNode = JSConstructNodeBase<IrOpcode::kJSConstruct>;
using JSConstructWithSpreadNode =
JSConstructNodeBase<IrOpcode::kJSConstructWithSpread>;
using JSConstructWithArrayLikeNode =
JSConstructNodeBase<IrOpcode::kJSConstructWithArrayLike>;
#undef DEFINE_INPUT_ACCESSORS
} // namespace compiler
......
......@@ -1526,12 +1526,21 @@ void ReduceBuiltin(JSGraph* jsgraph, Node* node, int builtin_index, int arity,
// The logic contained here is mirrored in Builtins::Generate_Adaptor.
// Keep these in sync.
const bool is_construct = (node->opcode() == IrOpcode::kJSConstruct);
STATIC_ASSERT(JSCallNode::TargetIndex() == JSConstructNode::TargetIndex());
Node* target = node->InputAt(JSCallNode::TargetIndex());
Node* target = NodeProperties::GetValueInput(node, 0);
Node* new_target = is_construct
? NodeProperties::GetValueInput(node, arity + 1)
: jsgraph->UndefinedConstant();
Node* new_target;
Zone* zone = jsgraph->zone();
if (node->opcode() == IrOpcode::kJSConstruct) {
// Unify representations between construct and call nodes.
// Remove new target and add receiver.
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
new_target = node->RemoveInput(JSConstructNode{node}.NewTargetIndex());
node->InsertInput(zone, JSCallNode::ReceiverIndex(),
jsgraph->UndefinedConstant());
} else {
new_target = jsgraph->UndefinedConstant();
}
// CPP builtins are implemented in C++, and we can inline it.
// CPP builtins create a builtin exit frame.
......@@ -1542,7 +1551,6 @@ void ReduceBuiltin(JSGraph* jsgraph, Node* node, int builtin_index, int arity,
has_builtin_exit_frame);
node->ReplaceInput(0, stub);
Zone* zone = jsgraph->zone();
const int argc = arity + BuiltinArguments::kNumExtraArgsWithReceiver;
Node* argc_node = jsgraph->Constant(argc);
......@@ -1552,24 +1560,8 @@ void ReduceBuiltin(JSGraph* jsgraph, Node* node, int builtin_index, int arity,
node->InsertInput(zone, 2, target);
node->InsertInput(zone, 3, argc_node);
node->InsertInput(zone, 4, jsgraph->PaddingConstant());
if (is_construct) {
// Unify representations between construct and call nodes.
// Remove new target and add receiver as a stack parameter.
Node* receiver = jsgraph->UndefinedConstant();
node->RemoveInput(argc);
node->InsertInput(zone, 5, receiver);
}
int cursor = arity + kStubAndReceiver + BuiltinArguments::kNumExtraArgs;
#else
if (is_construct) {
// Unify representations between construct and call nodes.
// Remove new target and add receiver as a stack parameter.
Node* receiver = jsgraph->UndefinedConstant();
node->RemoveInput(arity + 1);
node->InsertInput(zone, 1, receiver);
}
int cursor = arity + kStubAndReceiver;
node->InsertInput(zone, cursor++, jsgraph->PaddingConstant());
node->InsertInput(zone, cursor++, argc_node);
......@@ -1639,12 +1631,12 @@ Reduction JSTypedLowering::ReduceJSConstructForwardVarargs(Node* node) {
}
Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
ConstructParameters const& p = ConstructParametersOf(node->op());
JSConstructNode n(node);
ConstructParameters const& p = n.Parameters();
int const arity = p.arity_without_implicit_args();
Node* target = NodeProperties::GetValueInput(node, 0);
Node* target = n.target();
Type target_type = NodeProperties::GetType(target);
Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
Node* new_target = n.new_target();
// Check if {target} is a known JSFunction.
if (target_type.IsHeapConstant() &&
......@@ -1665,7 +1657,8 @@ Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
use_builtin_construct_stub
? BUILTIN_CODE(isolate(), JSBuiltinsConstructStub)
: BUILTIN_CODE(isolate(), JSConstructStubGeneric));
node->RemoveInput(arity + 1);
STATIC_ASSERT(JSConstructNode::kNewTargetIsLastInput);
node->RemoveInput(n.NewTargetIndex());
node->InsertInput(graph()->zone(), 0, jsgraph()->Constant(code));
node->InsertInput(graph()->zone(), 2, new_target);
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
......
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