| Index: src/compiler/js-inlining.cc
|
| diff --git a/src/compiler/js-inlining.cc b/src/compiler/js-inlining.cc
|
| index 782c5adafdc17a7443b37415c913b8ae362b96e8..65fce825b4af089287e6e4bd408a4c01ec278c8d 100644
|
| --- a/src/compiler/js-inlining.cc
|
| +++ b/src/compiler/js-inlining.cc
|
| @@ -347,29 +347,120 @@ bool IsNonConstructible(Handle<SharedFunctionInfo> shared_info) {
|
|
|
| } // namespace
|
|
|
| -
|
| -Reduction JSInliner::Reduce(Node* node) {
|
| - if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
|
| +// Determines whether the call target of the given call {node} is statically
|
| +// known and can be used as an inlining candidate. The {SharedFunctionInfo} of
|
| +// the call target is provided (the exact closure might be unknown).
|
| +bool JSInliner::DetermineCallTarget(
|
| + Node* node, Handle<SharedFunctionInfo>& shared_info_out) {
|
| + DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
|
| + HeapObjectMatcher match(node->InputAt(0));
|
|
|
| // This reducer can handle both normal function calls as well a constructor
|
| // calls whenever the target is a constant function object, as follows:
|
| // - JSCall(target:constant, receiver, args...)
|
| // - JSConstruct(target:constant, args..., new.target)
|
| + if (match.HasValue() && match.Value()->IsJSFunction()) {
|
| + Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
|
| +
|
| + // Disallow cross native-context inlining for now. This means that all parts
|
| + // of the resulting code will operate on the same global object. This also
|
| + // prevents cross context leaks, where we could inline functions from a
|
| + // different context and hold on to that context (and closure) from the code
|
| + // object.
|
| + // TODO(turbofan): We might want to revisit this restriction later when we
|
| + // have a need for this, and we know how to model different native contexts
|
| + // in the same graph in a compositional way.
|
| + if (function->context()->native_context() !=
|
| + info_->context()->native_context()) {
|
| + return false;
|
| + }
|
| +
|
| + shared_info_out = handle(function->shared());
|
| + return true;
|
| + }
|
| +
|
| + // This reducer can also handle calls where the target is statically known to
|
| + // be the result of a closure instantiation operation, as follows:
|
| + // - JSCall(JSCreateClosure[shared](context), receiver, args...)
|
| + // - JSConstruct(JSCreateClosure[shared](context), args..., new.target)
|
| + if (match.IsJSCreateClosure()) {
|
| + CreateClosureParameters const& p = CreateClosureParametersOf(match.op());
|
| +
|
| + // Disallow inlining in case the instantiation site was never run and hence
|
| + // the vector cell does not contain a valid feedback vector for the call
|
| + // target.
|
| + // TODO(turbofan): We might consider to eagerly create the feedback vector
|
| + // in such a case (in {DetermineCallContext} below) eventually.
|
| + FeedbackVectorSlot slot = p.feedback().slot();
|
| + Handle<Cell> cell(Cell::cast(p.feedback().vector()->Get(slot)));
|
| + if (!cell->value()->IsTypeFeedbackVector()) return false;
|
| +
|
| + shared_info_out = p.shared_info();
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// Determines statically known information about the call target (assuming that
|
| +// the call target is known according to {DetermineCallTarget} above). The
|
| +// following static information is provided:
|
| +// - context : The context (as SSA value) bound by the call target.
|
| +// - feedback_vector : The target is guaranteed to use this feedback vector.
|
| +void JSInliner::DetermineCallContext(
|
| + Node* node, Node*& context_out,
|
| + Handle<TypeFeedbackVector>& feedback_vector_out) {
|
| + DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
|
| HeapObjectMatcher match(node->InputAt(0));
|
| - if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange();
|
| - Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
|
|
|
| - return ReduceJSCall(node, function);
|
| + if (match.HasValue() && match.Value()->IsJSFunction()) {
|
| + Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
|
| +
|
| + // If the target function was never invoked, its literals array might not
|
| + // contain a feedback vector. We ensure at this point that it is created.
|
| + JSFunction::EnsureLiterals(function);
|
| +
|
| + // The inlinee specializes to the context from the JSFunction object.
|
| + context_out = jsgraph()->Constant(handle(function->context()));
|
| + feedback_vector_out = handle(function->feedback_vector());
|
| + return;
|
| + }
|
| +
|
| + if (match.IsJSCreateClosure()) {
|
| + CreateClosureParameters const& p = CreateClosureParametersOf(match.op());
|
| +
|
| + // Load the feedback vector of the target by looking up its vector cell at
|
| + // the instantiation site (we only decide to inline if it's populated).
|
| + FeedbackVectorSlot slot = p.feedback().slot();
|
| + Handle<Cell> cell(Cell::cast(p.feedback().vector()->Get(slot)));
|
| + DCHECK(cell->value()->IsTypeFeedbackVector());
|
| +
|
| + // The inlinee uses the locally provided context at instantiation.
|
| + context_out = NodeProperties::GetContextInput(match.node());
|
| + feedback_vector_out = handle(TypeFeedbackVector::cast(cell->value()));
|
| + return;
|
| + }
|
| +
|
| + // Must succeed.
|
| + UNREACHABLE();
|
| }
|
|
|
| -Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
|
| +Reduction JSInliner::Reduce(Node* node) {
|
| + if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
|
| + return ReduceJSCall(node);
|
| +}
|
| +
|
| +Reduction JSInliner::ReduceJSCall(Node* node) {
|
| DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
|
| + Handle<SharedFunctionInfo> shared_info;
|
| JSCallAccessor call(node);
|
| - Handle<SharedFunctionInfo> shared_info(function->shared());
|
| +
|
| + // Determine the call target.
|
| + if (!DetermineCallTarget(node, shared_info)) return NoChange();
|
|
|
| // Inlining is only supported in the bytecode pipeline.
|
| if (!info_->is_optimizing_from_bytecode()) {
|
| - TRACE("Inlining %s into %s is not supported in the deprecated pipeline\n",
|
| + TRACE("Not inlining %s into %s due to use of the deprecated pipeline\n",
|
| shared_info->DebugName()->ToCString().get(),
|
| info_->shared_info()->DebugName()->ToCString().get());
|
| return NoChange();
|
| @@ -410,22 +501,6 @@ Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
|
| return NoChange();
|
| }
|
|
|
| - // Disallow cross native-context inlining for now. This means that all parts
|
| - // of the resulting code will operate on the same global object.
|
| - // This also prevents cross context leaks for asm.js code, where we could
|
| - // inline functions from a different context and hold on to that context (and
|
| - // closure) from the code object.
|
| - // TODO(turbofan): We might want to revisit this restriction later when we
|
| - // have a need for this, and we know how to model different native contexts
|
| - // in the same graph in a compositional way.
|
| - if (function->context()->native_context() !=
|
| - info_->context()->native_context()) {
|
| - TRACE("Not inlining %s into %s because of different native contexts\n",
|
| - shared_info->DebugName()->ToCString().get(),
|
| - info_->shared_info()->DebugName()->ToCString().get());
|
| - return NoChange();
|
| - }
|
| -
|
| // TODO(turbofan): TranslatedState::GetAdaptedArguments() currently relies on
|
| // not inlining recursive functions. We might want to relax that at some
|
| // point.
|
| @@ -504,8 +579,10 @@ Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
|
| shared_info->DebugName()->ToCString().get(),
|
| info_->shared_info()->DebugName()->ToCString().get());
|
|
|
| - // If function was lazily compiled, its literals array may not yet be set up.
|
| - JSFunction::EnsureLiterals(function);
|
| + // Determine the targets feedback vector and its context.
|
| + Node* context;
|
| + Handle<TypeFeedbackVector> feedback_vector;
|
| + DetermineCallContext(node, context, feedback_vector);
|
|
|
| // Create the subgraph for the inlinee.
|
| Node* start;
|
| @@ -514,9 +591,8 @@ Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
|
| // Run the BytecodeGraphBuilder to create the subgraph.
|
| Graph::SubgraphScope scope(graph());
|
| BytecodeGraphBuilder graph_builder(
|
| - &zone, shared_info, handle(function->feedback_vector()),
|
| - BailoutId::None(), jsgraph(), call.frequency(), source_positions_,
|
| - inlining_id);
|
| + &zone, shared_info, feedback_vector, BailoutId::None(), jsgraph(),
|
| + call.frequency(), source_positions_, inlining_id);
|
| graph_builder.CreateGraph(false);
|
|
|
| // Extract the inlinee start/end nodes.
|
| @@ -593,12 +669,6 @@ Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
|
| 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).
|
| if (node->opcode() == IrOpcode::kJSCall &&
|
|
|