| Index: src/compiler/js-inlining.cc
|
| diff --git a/src/compiler/js-inlining.cc b/src/compiler/js-inlining.cc
|
| index f041698ab9d9c994fd3c16d261910db0bfe5de50..28365af906606442b4760274f4a2652a24ab7426 100644
|
| --- a/src/compiler/js-inlining.cc
|
| +++ b/src/compiler/js-inlining.cc
|
| @@ -34,36 +34,47 @@ namespace compiler {
|
| } while (false)
|
|
|
|
|
| -// Provides convenience accessors for calls to JS functions.
|
| -class JSCallFunctionAccessor {
|
| +// Provides convenience accessors for the common layout of nodes having either
|
| +// the {JSCallFunction} or the {JSCallConstruct} operator.
|
| +class JSCallAccessor {
|
| public:
|
| - explicit JSCallFunctionAccessor(Node* call) : call_(call) {
|
| - DCHECK_EQ(IrOpcode::kJSCallFunction, call->opcode());
|
| + explicit JSCallAccessor(Node* call) : call_(call) {
|
| + DCHECK(call->opcode() == IrOpcode::kJSCallFunction ||
|
| + call->opcode() == IrOpcode::kJSCallConstruct);
|
| }
|
|
|
| - Node* jsfunction() { return call_->InputAt(0); }
|
| -
|
| - Node* receiver() { return call_->InputAt(1); }
|
| + Node* target() {
|
| + // Both, {JSCallFunction} and {JSCallConstruct}, have same layout here.
|
| + return call_->InputAt(0);
|
| + }
|
|
|
| - Node* formal_argument(size_t index) {
|
| - DCHECK(index < formal_arguments());
|
| - return call_->InputAt(static_cast<int>(2 + index));
|
| + Node* receiver() {
|
| + DCHECK_EQ(IrOpcode::kJSCallFunction, call_->opcode());
|
| + return call_->InputAt(1);
|
| }
|
|
|
| - size_t formal_arguments() {
|
| - // {value_inputs} includes jsfunction and receiver.
|
| - size_t value_inputs = call_->op()->ValueInputCount();
|
| - DCHECK_GE(call_->InputCount(), 2);
|
| - return value_inputs - 2;
|
| + Node* original_constructor() {
|
| + DCHECK_EQ(IrOpcode::kJSCallConstruct, call_->opcode());
|
| + return call_->InputAt(formal_arguments() + 1);
|
| }
|
|
|
| Node* frame_state_before() {
|
| + DCHECK_EQ(IrOpcode::kJSCallFunction, call_->opcode());
|
| return NodeProperties::GetFrameStateInput(call_, 1);
|
| }
|
| +
|
| Node* frame_state_after() {
|
| + // Both, {JSCallFunction} and {JSCallConstruct}, have frame state after.
|
| return NodeProperties::GetFrameStateInput(call_, 0);
|
| }
|
|
|
| + int formal_arguments() {
|
| + // Both, {JSCallFunction} and {JSCallConstruct}, have two extra inputs:
|
| + // - JSCallConstruct: Includes target function and original constructor.
|
| + // - JSCallFunction: Includes target function and receiver.
|
| + return call_->op()->ValueInputCount() - 2;
|
| + }
|
| +
|
| private:
|
| Node* call_;
|
| };
|
| @@ -227,12 +238,13 @@ Reduction JSInliner::InlineCall(Node* call, Node* context, Node* frame_state,
|
| }
|
|
|
|
|
| -Node* JSInliner::CreateArgumentsAdaptorFrameState(
|
| - JSCallFunctionAccessor* call, Handle<SharedFunctionInfo> shared_info) {
|
| +Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
|
| + int parameter_count,
|
| + FrameStateType frame_state_type,
|
| + Handle<SharedFunctionInfo> shared) {
|
| const FrameStateFunctionInfo* state_info =
|
| jsgraph_->common()->CreateFrameStateFunctionInfo(
|
| - FrameStateType::kArgumentsAdaptor,
|
| - static_cast<int>(call->formal_arguments()) + 1, 0, shared_info,
|
| + frame_state_type, parameter_count + 1, 0, shared,
|
| CALL_MAINTAINS_NATIVE_CONTEXT);
|
|
|
| const Operator* op = jsgraph_->common()->FrameState(
|
| @@ -240,36 +252,37 @@ Node* JSInliner::CreateArgumentsAdaptorFrameState(
|
| const Operator* op0 = jsgraph_->common()->StateValues(0);
|
| Node* node0 = jsgraph_->graph()->NewNode(op0);
|
| NodeVector params(local_zone_);
|
| - params.push_back(call->receiver());
|
| - for (size_t argument = 0; argument != call->formal_arguments(); ++argument) {
|
| - params.push_back(call->formal_argument(argument));
|
| + for (int parameter = 0; parameter < parameter_count + 1; ++parameter) {
|
| + params.push_back(node->InputAt(1 + parameter));
|
| }
|
| const Operator* op_param =
|
| jsgraph_->common()->StateValues(static_cast<int>(params.size()));
|
| Node* params_node = jsgraph_->graph()->NewNode(
|
| op_param, static_cast<int>(params.size()), ¶ms.front());
|
| - return jsgraph_->graph()->NewNode(
|
| - op, params_node, node0, node0, jsgraph_->UndefinedConstant(),
|
| - call->jsfunction(), call->frame_state_after());
|
| + return jsgraph_->graph()->NewNode(op, params_node, node0, node0,
|
| + jsgraph_->UndefinedConstant(),
|
| + node->InputAt(0), outer_frame_state);
|
| }
|
|
|
|
|
| Reduction JSInliner::Reduce(Node* node) {
|
| - if (node->opcode() != IrOpcode::kJSCallFunction) return NoChange();
|
| + if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
|
|
|
| - JSCallFunctionAccessor call(node);
|
| - HeapObjectMatcher match(call.jsfunction());
|
| + // This reducer can handle both normal function calls as well a constructor
|
| + // calls whenever the target is a constant function object, as follows:
|
| + // - JSCallFunction(target:constant, receiver, args...)
|
| + // - JSCallConstruct(target:constant, args..., new.target)
|
| + HeapObjectMatcher match(node->InputAt(0));
|
| if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange();
|
| Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
|
|
|
| - return ReduceJSCallFunction(node, function);
|
| + return ReduceJSCall(node, function);
|
| }
|
|
|
|
|
| -Reduction JSInliner::ReduceJSCallFunction(Node* node,
|
| - Handle<JSFunction> function) {
|
| - DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
|
| - JSCallFunctionAccessor call(node);
|
| +Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
|
| + DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
|
| + JSCallAccessor call(node);
|
|
|
| if (!function->shared()->IsInlineable()) {
|
| // Function must be inlineable.
|
| @@ -359,7 +372,7 @@ Reduction JSInliner::ReduceJSCallFunction(Node* node,
|
|
|
| // In strong mode, in case of too few arguments we need to throw a TypeError
|
| // so we must not inline this call.
|
| - size_t parameter_count = info.literal()->parameter_count();
|
| + int parameter_count = info.literal()->parameter_count();
|
| if (is_strong(info.language_mode()) &&
|
| call.formal_arguments() < parameter_count) {
|
| TRACE("Not inlining %s into %s because too few arguments for strong mode\n",
|
| @@ -425,12 +438,6 @@ Reduction JSInliner::ReduceJSCallFunction(Node* node,
|
| graph_reducer.ReduceGraph();
|
| }
|
|
|
| - // The inlinee specializes to the context from the JSFunction object.
|
| - // TODO(turbofan): We might want to load the context from the JSFunction at
|
| - // runtime in case we only know the SharedFunctionInfo once we have dynamic
|
| - // type feedback in the compiler.
|
| - Node* context = jsgraph_->Constant(handle(function->context()));
|
| -
|
| CopyVisitor visitor(&graph, jsgraph_->graph(), &zone);
|
| visitor.CopyGraph();
|
|
|
| @@ -438,13 +445,60 @@ Reduction JSInliner::ReduceJSCallFunction(Node* node,
|
| Node* end = visitor.GetCopy(graph.end());
|
| Node* frame_state = call.frame_state_after();
|
|
|
| + // Insert nodes around the call that model the behavior required for a
|
| + // constructor dispatch and turn the constructor call into a regular call.
|
| + // This models the behavior usually accomplished by our {JSConstructStub}.
|
| + // Note that the context has to be the callers context (input to call node).
|
| + if (node->opcode() == IrOpcode::kJSCallConstruct) {
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* context = NodeProperties::GetContextInput(node);
|
| + Node* create = jsgraph_->graph()->NewNode(jsgraph_->javascript()->Create(),
|
| + call.target(), context, effect);
|
| + NodeProperties::ReplaceEffectInput(node, create);
|
| + // TODO(4544): For now {JSCreate} requires the actual constructor to
|
| + // coincide with the original constructor. Adapt JSGenericLowering to fix.
|
| + // Also Runtime_GetOriginalConstructor depends on this for now. Fix as well!
|
| + CHECK_EQ(call.target(), call.original_constructor());
|
| + // TODO(4544): For derived constructors we should not allocate an implicit
|
| + // receiver and also the return value should not be checked afterwards.
|
| + CHECK(!IsClassConstructor(function->shared()->kind()));
|
| + // Swizzle the inputs of the {JSCallConstruct} node to look like inputs to
|
| + // any {JSCallFunction} node so that the rest of the inlining machinery
|
| + // behaves as if we were dealing with a regular function invocation.
|
| + node->RemoveInput(call.formal_arguments() + 1); // Drop new.target.
|
| + node->InsertInput(jsgraph_->graph()->zone(), 1, create);
|
| + // Insert a check of the return value to determine whether the return value
|
| + // or the implicit receiver should be selected as a result of the call.
|
| + Node* check = jsgraph_->graph()->NewNode(
|
| + jsgraph_->javascript()->CallRuntime(Runtime::kInlineIsSpecObject, 1),
|
| + node, context, node, start);
|
| + Node* select = jsgraph_->graph()->NewNode(
|
| + jsgraph_->common()->Select(kMachAnyTagged), check, node, create);
|
| + NodeProperties::ReplaceUses(node, select, check, node, node);
|
| + NodeProperties::ReplaceValueInput(select, node, 1);
|
| + NodeProperties::ReplaceValueInput(check, node, 0);
|
| + NodeProperties::ReplaceEffectInput(check, 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(),
|
| + FrameStateType::kConstructStub, info.shared_info());
|
| + }
|
| +
|
| + // The inlinee specializes to the context from the JSFunction object.
|
| + // TODO(turbofan): We might want to load the context from the JSFunction at
|
| + // runtime in case we only know the SharedFunctionInfo once we have dynamic
|
| + // type feedback in the compiler.
|
| + Node* context = jsgraph_->Constant(handle(function->context()));
|
| +
|
| // Insert a JSConvertReceiver node for sloppy callees. Note that the context
|
| // passed into this node has to be the callees context (loaded above). Note
|
| // that the frame state passed to the JSConvertReceiver must be the frame
|
| // state _before_ the call; it is not necessary to fiddle with the receiver
|
| // in that frame state tho, as the conversion of the receiver can be repeated
|
| // any number of times, it's not observable.
|
| - if (is_sloppy(info.language_mode()) && !function->shared()->native()) {
|
| + if (node->opcode() == IrOpcode::kJSCallFunction &&
|
| + is_sloppy(info.language_mode()) && !function->shared()->native()) {
|
| const CallFunctionParameters& p = CallFunctionParametersOf(node->op());
|
| Node* effect = NodeProperties::GetEffectInput(node);
|
| Node* convert = jsgraph_->graph()->NewNode(
|
| @@ -457,10 +511,11 @@ Reduction JSInliner::ReduceJSCallFunction(Node* node,
|
| // Insert argument adaptor frame if required. The callees formal parameter
|
| // count (i.e. value outputs of start node minus target, receiver, num args
|
| // and context) have to match the number of arguments passed to the call.
|
| - DCHECK_EQ(static_cast<int>(parameter_count),
|
| - start->op()->ValueOutputCount() - 4);
|
| + DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 4);
|
| if (call.formal_arguments() != parameter_count) {
|
| - frame_state = CreateArgumentsAdaptorFrameState(&call, info.shared_info());
|
| + frame_state = CreateArtificialFrameState(
|
| + node, frame_state, call.formal_arguments(),
|
| + FrameStateType::kArgumentsAdaptor, info.shared_info());
|
| }
|
|
|
| return InlineCall(node, context, frame_state, start, end);
|
|
|