js-inlining-heuristic.cc 5.21 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/js-inlining-heuristic.h"

7
#include "src/compiler.h"
8 9 10 11 12 13 14 15
#include "src/compiler/node-matchers.h"
#include "src/objects-inl.h"

namespace v8 {
namespace internal {
namespace compiler {

Reduction JSInliningHeuristic::Reduce(Node* node) {
16
  if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
17

18 19 20 21
  // Check if we already saw that {node} before, and if so, just skip it.
  if (seen_.find(node->id()) != seen_.end()) return NoChange();
  seen_.insert(node->id());

22 23 24 25 26 27 28
  Node* callee = node->InputAt(0);
  HeapObjectMatcher match(callee);
  if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange();
  Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());

  // Functions marked with %SetForceInlineFlag are immediately inlined.
  if (function->shared()->force_inline()) {
29
    return inliner_.ReduceJSCall(node, function);
30 31
  }

32 33 34 35 36 37 38
  // Handling of special inlining modes right away:
  //  - For restricted inlining: stop all handling at this point.
  //  - For stressing inlining: immediately handle all functions.
  switch (mode_) {
    case kRestrictedInlining:
      return NoChange();
    case kStressInlining:
39
      return inliner_.ReduceJSCall(node, function);
40 41 42 43 44 45 46 47 48 49 50
    case kGeneralInlining:
      break;
  }

  // ---------------------------------------------------------------------------
  // Everything below this line is part of the inlining heuristic.
  // ---------------------------------------------------------------------------

  // Built-in functions are handled by the JSBuiltinReducer.
  if (function->shared()->HasBuiltinFunctionId()) return NoChange();

51 52 53
  // Don't inline builtins.
  if (function->shared()->IsBuiltin()) return NoChange();

54 55 56 57 58 59 60 61 62 63
  // Quick check on source code length to avoid parsing large candidate.
  if (function->shared()->SourceSize() > FLAG_max_inlined_source_size) {
    return NoChange();
  }

  // Quick check on the size of the AST to avoid parsing large candidate.
  if (function->shared()->ast_node_count() > FLAG_max_inlined_nodes) {
    return NoChange();
  }

64 65 66 67
  // Avoid inlining within or across the boundary of asm.js code.
  if (info_->shared_info()->asm_function()) return NoChange();
  if (function->shared()->asm_function()) return NoChange();

68 69
  // Stop inlinining once the maximum allowed level is reached.
  int level = 0;
70
  for (Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
71 72 73 74 75
       frame_state->opcode() == IrOpcode::kFrameState;
       frame_state = NodeProperties::GetFrameStateInput(frame_state, 0)) {
    if (++level > FLAG_max_inlining_levels) return NoChange();
  }

76
  // Gather feedback on how often this call site has been hit before.
77
  int calls = -1;  // Same default as CallICNexus::ExtractCallCount.
78
  // TODO(turbofan): We also want call counts for constructor calls.
79 80 81 82 83 84
  if (node->opcode() == IrOpcode::kJSCallFunction) {
    CallFunctionParameters p = CallFunctionParametersOf(node->op());
    if (p.feedback().IsValid()) {
      CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
      calls = nexus.ExtractCallCount();
    }
85
  }
86 87 88 89 90 91

  // ---------------------------------------------------------------------------
  // Everything above this line is part of the inlining heuristic.
  // ---------------------------------------------------------------------------

  // In the general case we remember the candidate for later.
92
  candidates_.insert({function, node, calls});
93 94 95 96
  return NoChange();
}


97
void JSInliningHeuristic::Finalize() {
98
  if (candidates_.empty()) return;  // Nothing to do without candidates.
99 100
  if (FLAG_trace_turbo_inlining) PrintCandidates();

101 102 103
  // We inline at most one candidate in every iteration of the fixpoint.
  // This is to ensure that we don't consume the full inlining budget
  // on things that aren't called very often.
104 105 106 107 108 109
  // TODO(bmeurer): Use std::priority_queue instead of std::set here.
  while (!candidates_.empty()) {
    if (cumulative_count_ > FLAG_max_inlined_nodes_cumulative) return;
    auto i = candidates_.begin();
    Candidate candidate = *i;
    candidates_.erase(i);
110 111 112 113 114 115 116
    // Make sure we don't try to inline dead candidate nodes.
    if (!candidate.node->IsDead()) {
      Reduction r = inliner_.ReduceJSCall(candidate.node, candidate.function);
      if (r.Changed()) {
        cumulative_count_ += candidate.function->shared()->ast_node_count();
        return;
      }
117 118
    }
  }
119 120 121
}


122 123
bool JSInliningHeuristic::CandidateCompare::operator()(
    const Candidate& left, const Candidate& right) const {
124 125 126 127
  if (left.calls != right.calls) {
    return left.calls > right.calls;
  }
  return left.node < right.node;
128 129 130 131 132 133 134 135 136 137 138 139
}


void JSInliningHeuristic::PrintCandidates() {
  PrintF("Candidates for inlining (size=%zu):\n", candidates_.size());
  for (const Candidate& candidate : candidates_) {
    PrintF("  id:%d, calls:%d, size[source]:%d, size[ast]:%d / %s\n",
           candidate.node->id(), candidate.calls,
           candidate.function->shared()->SourceSize(),
           candidate.function->shared()->ast_node_count(),
           candidate.function->shared()->DebugName()->ToCString().get());
  }
140 141 142 143 144
}

}  // namespace compiler
}  // namespace internal
}  // namespace v8