1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/compiler/js-inlining-heuristic.h"
6
7#include "src/compiler.h"
8#include "src/compiler/node-matchers.h"
9#include "src/objects-inl.h"
10
11namespace v8 {
12namespace internal {
13namespace compiler {
14
15Reduction JSInliningHeuristic::Reduce(Node* node) {
16  if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
17
18  // Check if we already saw that {node} before, and if so, just skip it.
19  if (seen_.find(node->id()) != seen_.end()) return NoChange();
20  seen_.insert(node->id());
21
22  Node* callee = node->InputAt(0);
23  HeapObjectMatcher match(callee);
24  if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange();
25  Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
26
27  // Functions marked with %SetForceInlineFlag are immediately inlined.
28  if (function->shared()->force_inline()) {
29    return inliner_.ReduceJSCall(node, function);
30  }
31
32  // Handling of special inlining modes right away:
33  //  - For restricted inlining: stop all handling at this point.
34  //  - For stressing inlining: immediately handle all functions.
35  switch (mode_) {
36    case kRestrictedInlining:
37      return NoChange();
38    case kStressInlining:
39      return inliner_.ReduceJSCall(node, function);
40    case kGeneralInlining:
41      break;
42  }
43
44  // ---------------------------------------------------------------------------
45  // Everything below this line is part of the inlining heuristic.
46  // ---------------------------------------------------------------------------
47
48  // Built-in functions are handled by the JSBuiltinReducer.
49  if (function->shared()->HasBuiltinFunctionId()) return NoChange();
50
51  // Don't inline builtins.
52  if (function->shared()->IsBuiltin()) return NoChange();
53
54  // Quick check on source code length to avoid parsing large candidate.
55  if (function->shared()->SourceSize() > FLAG_max_inlined_source_size) {
56    return NoChange();
57  }
58
59  // Quick check on the size of the AST to avoid parsing large candidate.
60  if (function->shared()->ast_node_count() > FLAG_max_inlined_nodes) {
61    return NoChange();
62  }
63
64  // Avoid inlining within or across the boundary of asm.js code.
65  if (info_->shared_info()->asm_function()) return NoChange();
66  if (function->shared()->asm_function()) return NoChange();
67
68  // Stop inlinining once the maximum allowed level is reached.
69  int level = 0;
70  for (Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
71       frame_state->opcode() == IrOpcode::kFrameState;
72       frame_state = NodeProperties::GetFrameStateInput(frame_state, 0)) {
73    if (++level > FLAG_max_inlining_levels) return NoChange();
74  }
75
76  // Gather feedback on how often this call site has been hit before.
77  int calls = -1;  // Same default as CallICNexus::ExtractCallCount.
78  if (node->opcode() == IrOpcode::kJSCallFunction) {
79    CallFunctionParameters p = CallFunctionParametersOf(node->op());
80    if (p.feedback().IsValid()) {
81      CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
82      calls = nexus.ExtractCallCount();
83    }
84  } else {
85    DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode());
86    CallConstructParameters p = CallConstructParametersOf(node->op());
87    if (p.feedback().IsValid()) {
88      int const extra_index =
89          p.feedback().vector()->GetIndex(p.feedback().slot()) + 1;
90      Handle<Object> feedback_extra(p.feedback().vector()->get(extra_index),
91                                    function->GetIsolate());
92      if (feedback_extra->IsSmi()) {
93        calls = Handle<Smi>::cast(feedback_extra)->value();
94      }
95    }
96  }
97
98  // ---------------------------------------------------------------------------
99  // Everything above this line is part of the inlining heuristic.
100  // ---------------------------------------------------------------------------
101
102  // In the general case we remember the candidate for later.
103  candidates_.insert({function, node, calls});
104  return NoChange();
105}
106
107
108void JSInliningHeuristic::Finalize() {
109  if (candidates_.empty()) return;  // Nothing to do without candidates.
110  if (FLAG_trace_turbo_inlining) PrintCandidates();
111
112  // We inline at most one candidate in every iteration of the fixpoint.
113  // This is to ensure that we don't consume the full inlining budget
114  // on things that aren't called very often.
115  // TODO(bmeurer): Use std::priority_queue instead of std::set here.
116  while (!candidates_.empty()) {
117    if (cumulative_count_ > FLAG_max_inlined_nodes_cumulative) return;
118    auto i = candidates_.begin();
119    Candidate candidate = *i;
120    candidates_.erase(i);
121    // Make sure we don't try to inline dead candidate nodes.
122    if (!candidate.node->IsDead()) {
123      Reduction r = inliner_.ReduceJSCall(candidate.node, candidate.function);
124      if (r.Changed()) {
125        cumulative_count_ += candidate.function->shared()->ast_node_count();
126        return;
127      }
128    }
129  }
130}
131
132
133bool JSInliningHeuristic::CandidateCompare::operator()(
134    const Candidate& left, const Candidate& right) const {
135  if (left.calls != right.calls) {
136    return left.calls > right.calls;
137  }
138  return left.node < right.node;
139}
140
141
142void JSInliningHeuristic::PrintCandidates() {
143  PrintF("Candidates for inlining (size=%zu):\n", candidates_.size());
144  for (const Candidate& candidate : candidates_) {
145    PrintF("  id:%d, calls:%d, size[source]:%d, size[ast]:%d / %s\n",
146           candidate.node->id(), candidate.calls,
147           candidate.function->shared()->SourceSize(),
148           candidate.function->shared()->ast_node_count(),
149           candidate.function->shared()->DebugName()->ToCString().get());
150  }
151}
152
153}  // namespace compiler
154}  // namespace internal
155}  // namespace v8
156