LCOV - code coverage report
Current view: top level - src/compiler - js-inlining-heuristic.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 218 253 86.2 %
Date: 2017-10-20 Functions: 13 17 76.5 %

          Line data    Source code
       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/compilation-info.h"
       8             : #include "src/compiler/common-operator.h"
       9             : #include "src/compiler/node-matchers.h"
      10             : #include "src/compiler/simplified-operator.h"
      11             : #include "src/objects-inl.h"
      12             : 
      13             : namespace v8 {
      14             : namespace internal {
      15             : namespace compiler {
      16             : 
      17             : #define TRACE(...)                                      \
      18             :   do {                                                  \
      19             :     if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \
      20             :   } while (false)
      21             : 
      22             : namespace {
      23             : 
      24      600962 : int CollectFunctions(Node* node, Handle<JSFunction>* functions,
      25             :                      int functions_size, Handle<SharedFunctionInfo>& shared) {
      26             :   DCHECK_NE(0, functions_size);
      27             :   HeapObjectMatcher m(node);
      28      902381 :   if (m.HasValue() && m.Value()->IsJSFunction()) {
      29      300015 :     functions[0] = Handle<JSFunction>::cast(m.Value());
      30      300015 :     return 1;
      31             :   }
      32      300947 :   if (m.IsPhi()) {
      33       23188 :     int const value_input_count = m.node()->op()->ValueInputCount();
      34       23188 :     if (value_input_count > functions_size) return 0;
      35       22855 :     for (int n = 0; n < value_input_count; ++n) {
      36             :       HeapObjectMatcher m(node->InputAt(n));
      37       68810 :       if (!m.HasValue() || !m.Value()->IsJSFunction()) return 0;
      38       22855 :       functions[n] = Handle<JSFunction>::cast(m.Value());
      39             :     }
      40             :     return value_input_count;
      41             :   }
      42      277759 :   if (m.IsJSCreateClosure()) {
      43       33302 :     CreateClosureParameters const& p = CreateClosureParametersOf(m.op());
      44       33301 :     functions[0] = Handle<JSFunction>::null();
      45       33301 :     shared = p.shared_info();
      46       33301 :     return 1;
      47             :   }
      48             :   return 0;
      49             : }
      50             : 
      51      333571 : bool CanInlineFunction(Handle<SharedFunctionInfo> shared) {
      52             :   // Built-in functions are handled by the JSBuiltinReducer.
      53      333571 :   if (shared->HasBuiltinFunctionId()) return false;
      54             : 
      55             :   // Only choose user code for inlining.
      56      261490 :   if (!shared->IsUserJavaScript()) return false;
      57             : 
      58             :   // If there is no bytecode array, it is either not compiled or it is compiled
      59             :   // with WebAssembly for the asm.js pipeline. In either case we don't want to
      60             :   // inline.
      61      202779 :   if (!shared->HasBytecodeArray()) return false;
      62             : 
      63             :   // Quick check on the size of the bytecode to avoid inlining large functions.
      64      196720 :   if (shared->bytecode_array()->length() > FLAG_max_inlined_bytecode_size) {
      65             :     return false;
      66             :   }
      67             : 
      68      196479 :   return true;
      69             : }
      70             : 
      71      333571 : bool IsSmallInlineFunction(Handle<SharedFunctionInfo> shared) {
      72             :   // Forcibly inline small functions.
      73             :   // Don't forcibly inline functions that weren't compiled yet.
      74      541775 :   if (shared->HasBytecodeArray() && shared->bytecode_array()->length() <=
      75             :                                         FLAG_max_inlined_bytecode_size_small) {
      76             :     return true;
      77             :   }
      78      268569 :   return false;
      79             : }
      80             : 
      81             : }  // namespace
      82             : 
      83    32215636 : Reduction JSInliningHeuristic::Reduce(Node* node) {
      84    30740601 :   if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
      85             : 
      86             :   // Check if we already saw that {node} before, and if so, just skip it.
      87     1358665 :   if (seen_.find(node->id()) != seen_.end()) return NoChange();
      88     1201925 :   seen_.insert(node->id());
      89             : 
      90             :   // Check if the {node} is an appropriate candidate for inlining.
      91             :   Node* callee = node->InputAt(0);
      92             :   Candidate candidate;
      93      600963 :   candidate.node = node;
      94             :   candidate.num_functions = CollectFunctions(
      95      600963 :       callee, candidate.functions, kMaxCallPolymorphism, candidate.shared_info);
      96      600962 :   if (candidate.num_functions == 0) {
      97             :     return NoChange();
      98      333410 :   } else if (candidate.num_functions > 1 && !FLAG_polymorphic_inlining) {
      99           0 :     TRACE(
     100             :         "Not considering call site #%d:%s, because polymorphic inlining "
     101             :         "is disabled\n",
     102             :         node->id(), node->op()->mnemonic());
     103             :     return NoChange();
     104             :   }
     105             : 
     106             :   bool can_inline = false, small_inline = true;
     107      333410 :   candidate.total_size = 0;
     108      333410 :   Node* frame_state = NodeProperties::GetFrameStateInput(node);
     109      333571 :   FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
     110             :   Handle<SharedFunctionInfo> frame_shared_info;
     111      666982 :   for (int i = 0; i < candidate.num_functions; ++i) {
     112             :     Handle<SharedFunctionInfo> shared =
     113      333571 :         candidate.functions[i].is_null()
     114             :             ? candidate.shared_info
     115      333571 :             : handle(candidate.functions[i]->shared());
     116      333571 :     candidate.can_inline_function[i] = CanInlineFunction(shared);
     117             :     // Do not allow direct recursion i.e. f() -> f(). We still allow indirect
     118             :     // recurion like f() -> g() -> f(). The indirect recursion is helpful in
     119             :     // cases where f() is a small dispatch function that calls the appropriate
     120             :     // function. In the case of direct recursion, we only have some static
     121             :     // information for the first level of inlining and it may not be that useful
     122             :     // to just inline one level in recursive calls. In some cases like tail
     123             :     // recursion we may benefit from recursive inlining, if we have additional
     124             :     // analysis that converts them to iterative implementations. Though it is
     125             :     // not obvious if such an anlysis is needed.
     126      667093 :     if (frame_info.shared_info().ToHandle(&frame_shared_info) &&
     127             :         *frame_shared_info == *shared) {
     128        1858 :       TRACE("Not considering call site #%d:%s, because of recursive inlining\n",
     129             :             node->id(), node->op()->mnemonic());
     130        1858 :       candidate.can_inline_function[i] = false;
     131             :     }
     132      333571 :     if (candidate.can_inline_function[i]) {
     133             :       can_inline = true;
     134      194986 :       candidate.total_size += shared->bytecode_array()->length();
     135             :     }
     136      333571 :     if (!IsSmallInlineFunction(shared)) {
     137             :       small_inline = false;
     138             :     }
     139             :   }
     140      333411 :   if (!can_inline) return NoChange();
     141             : 
     142             :   // Stop inlining once the maximum allowed level is reached.
     143             :   int level = 0;
     144      922596 :   for (Node* frame_state = NodeProperties::GetFrameStateInput(node);
     145             :        frame_state->opcode() == IrOpcode::kFrameState;
     146             :        frame_state = NodeProperties::GetFrameStateInput(frame_state)) {
     147      266558 :     FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
     148      266558 :     if (FrameStateFunctionInfo::IsJSFunctionType(frame_info.type())) {
     149      237267 :       if (++level > FLAG_max_inlining_levels) {
     150          98 :         TRACE(
     151             :             "Not considering call site #%d:%s, because inlining depth "
     152             :             "%d exceeds maximum allowed level %d\n",
     153             :             node->id(), node->op()->mnemonic(), level,
     154             :             FLAG_max_inlining_levels);
     155             :         return NoChange();
     156             :       }
     157             :     }
     158             :   }
     159             : 
     160             :   // Gather feedback on how often this call site has been hit before.
     161      194740 :   if (node->opcode() == IrOpcode::kJSCall) {
     162      183590 :     CallParameters const p = CallParametersOf(node->op());
     163      183590 :     candidate.frequency = p.frequency();
     164             :   } else {
     165       11150 :     ConstructParameters const p = ConstructParametersOf(node->op());
     166       11150 :     candidate.frequency = p.frequency();
     167             :   }
     168             : 
     169             :   // Handling of special inlining modes right away:
     170             :   //  - For restricted inlining: stop all handling at this point.
     171             :   //  - For stressing inlining: immediately handle all functions.
     172      194740 :   switch (mode_) {
     173             :     case kRestrictedInlining:
     174             :       return NoChange();
     175             :     case kStressInlining:
     176           0 :       return InlineCandidate(candidate, false);
     177             :     case kGeneralInlining:
     178             :       break;
     179             :   }
     180             : 
     181             :   // Don't consider a {candidate} whose frequency is below the
     182             :   // threshold, i.e. a call site that is only hit once every N
     183             :   // invocations of the caller.
     184      385594 :   if (candidate.frequency.IsKnown() &&
     185      191463 :       candidate.frequency.value() < FLAG_min_inlining_frequency) {
     186             :     return NoChange();
     187             :   }
     188             : 
     189             :   // Forcibly inline small functions here. In the case of polymorphic inlining
     190             :   // small_inline is set only when all functions are small.
     191       68668 :   if (small_inline) {
     192       12510 :     TRACE("Inlining small function(s) at call site #%d:%s\n", node->id(),
     193             :           node->op()->mnemonic());
     194       12510 :     return InlineCandidate(candidate, true);
     195             :   }
     196             : 
     197             :   // In the general case we remember the candidate for later.
     198             :   candidates_.insert(candidate);
     199             :   return NoChange();
     200             : }
     201             : 
     202      492665 : void JSInliningHeuristic::Finalize() {
     203      492665 :   if (candidates_.empty()) return;  // Nothing to do without candidates.
     204       50462 :   if (FLAG_trace_turbo_inlining) PrintCandidates();
     205             : 
     206             :   // We inline at most one candidate in every iteration of the fixpoint.
     207             :   // This is to ensure that we don't consume the full inlining budget
     208             :   // on things that aren't called very often.
     209             :   // TODO(bmeurer): Use std::priority_queue instead of std::set here.
     210       57335 :   while (!candidates_.empty()) {
     211             :     auto i = candidates_.begin();
     212       56158 :     Candidate candidate = *i;
     213             :     candidates_.erase(i);
     214             : 
     215             :     // Make sure we have some extra budget left, so that any small functions
     216             :     // exposed by this function would be given a chance to inline.
     217             :     double size_of_candidate =
     218       56158 :         candidate.total_size * FLAG_reserve_inline_budget_scale_factor;
     219       56158 :     int total_size = cumulative_count_ + static_cast<int>(size_of_candidate);
     220       56158 :     if (total_size > FLAG_max_inlined_bytecode_size_cumulative) {
     221             :       // Try if any smaller functions are available to inline.
     222        6528 :       continue;
     223             :     }
     224             : 
     225             :     // Make sure we don't try to inline dead candidate nodes.
     226       99260 :     if (!candidate.node->IsDead()) {
     227       49630 :       Reduction const reduction = InlineCandidate(candidate, false);
     228       49630 :       if (reduction.Changed()) return;
     229             :     }
     230             :   }
     231             : }
     232             : 
     233             : namespace {
     234             : 
     235             : struct NodeAndIndex {
     236             :   Node* node;
     237             :   int index;
     238             : };
     239             : 
     240         140 : bool CollectStateValuesOwnedUses(Node* node, Node* state_values,
     241             :                                  NodeAndIndex* uses_buffer, size_t* use_count,
     242             :                                  size_t max_uses) {
     243             :   // Only accumulate states that are not shared with other users.
     244         140 :   if (state_values->UseCount() > 1) return true;
     245         722 :   for (int i = 0; i < state_values->InputCount(); i++) {
     246         310 :     Node* input = state_values->InputAt(i);
     247         310 :     if (input->opcode() == IrOpcode::kStateValues) {
     248           0 :       if (!CollectStateValuesOwnedUses(node, input, uses_buffer, use_count,
     249           0 :                                        max_uses)) {
     250             :         return false;
     251             :       }
     252         310 :     } else if (input == node) {
     253          70 :       if (*use_count >= max_uses) return false;
     254          70 :       uses_buffer[*use_count] = {state_values, i};
     255          70 :       (*use_count)++;
     256             :     }
     257             :   }
     258             :   return true;
     259             : }
     260             : 
     261             : }  // namespace
     262             : 
     263         274 : Node* JSInliningHeuristic::DuplicateStateValuesAndRename(Node* state_values,
     264             :                                                          Node* from, Node* to,
     265             :                                                          StateCloneMode mode) {
     266             :   // Only rename in states that are not shared with other users. This needs to
     267             :   // be in sync with the condition in {CollectStateValuesOwnedUses}.
     268         274 :   if (state_values->UseCount() > 1) return state_values;
     269         236 :   Node* copy = mode == kChangeInPlace ? state_values : nullptr;
     270        2128 :   for (int i = 0; i < state_values->InputCount(); i++) {
     271         828 :     Node* input = state_values->InputAt(i);
     272             :     Node* processed;
     273         828 :     if (input->opcode() == IrOpcode::kStateValues) {
     274           0 :       processed = DuplicateStateValuesAndRename(input, from, to, mode);
     275         828 :     } else if (input == from) {
     276             :       processed = to;
     277             :     } else {
     278             :       processed = input;
     279             :     }
     280         828 :     if (processed != input) {
     281         204 :       if (!copy) {
     282         134 :         copy = graph()->CloneNode(state_values);
     283             :       }
     284         204 :       copy->ReplaceInput(i, processed);
     285             :     }
     286             :   }
     287         236 :   return copy ? copy : state_values;
     288             : }
     289             : 
     290             : namespace {
     291             : 
     292         140 : bool CollectFrameStateUniqueUses(Node* node, Node* frame_state,
     293             :                                  NodeAndIndex* uses_buffer, size_t* use_count,
     294             :                                  size_t max_uses) {
     295             :   // Only accumulate states that are not shared with other users.
     296         140 :   if (frame_state->UseCount() > 1) return true;
     297         140 :   if (frame_state->InputAt(kFrameStateStackInput) == node) {
     298           0 :     if (*use_count >= max_uses) return false;
     299           0 :     uses_buffer[*use_count] = {frame_state, kFrameStateStackInput};
     300           0 :     (*use_count)++;
     301             :   }
     302         140 :   if (!CollectStateValuesOwnedUses(node,
     303             :                                    frame_state->InputAt(kFrameStateLocalsInput),
     304         140 :                                    uses_buffer, use_count, max_uses)) {
     305             :     return false;
     306             :   }
     307         140 :   return true;
     308             : }
     309             : 
     310             : }  // namespace
     311             : 
     312         408 : Node* JSInliningHeuristic::DuplicateFrameStateAndRename(Node* frame_state,
     313             :                                                         Node* from, Node* to,
     314             :                                                         StateCloneMode mode) {
     315             :   // Only rename in states that are not shared with other users. This needs to
     316             :   // be in sync with the condition in {DuplicateFrameStateAndRename}.
     317         408 :   if (frame_state->UseCount() > 1) return frame_state;
     318         274 :   Node* copy = mode == kChangeInPlace ? frame_state : nullptr;
     319         274 :   if (frame_state->InputAt(kFrameStateStackInput) == from) {
     320           0 :     if (!copy) {
     321           0 :       copy = graph()->CloneNode(frame_state);
     322             :     }
     323           0 :     copy->ReplaceInput(kFrameStateStackInput, to);
     324             :   }
     325             :   Node* locals = frame_state->InputAt(kFrameStateLocalsInput);
     326         274 :   Node* new_locals = DuplicateStateValuesAndRename(locals, from, to, mode);
     327         274 :   if (new_locals != locals) {
     328         134 :     if (!copy) {
     329         134 :       copy = graph()->CloneNode(frame_state);
     330             :     }
     331         134 :     copy->ReplaceInput(kFrameStateLocalsInput, new_locals);
     332             :   }
     333         274 :   return copy ? copy : frame_state;
     334             : }
     335             : 
     336         281 : bool JSInliningHeuristic::TryReuseDispatch(Node* node, Node* callee,
     337             :                                            Candidate const& candidate,
     338             :                                            Node** if_successes, Node** calls,
     339         280 :                                            Node** inputs, int input_count) {
     340             :   // We will try to reuse the control flow branch created for computing
     341             :   // the {callee} target of the call. We only reuse the branch if there
     342             :   // is no side-effect between the call and the branch, and if the callee is
     343             :   // only used as the target (and possibly also in the related frame states).
     344             : 
     345          77 :   int const num_calls = candidate.num_functions;
     346             : 
     347             :   DCHECK_EQ(IrOpcode::kPhi, callee->opcode());
     348             :   DCHECK_EQ(num_calls, callee->op()->ValueInputCount());
     349             : 
     350             :   // We are trying to match the following pattern:
     351             :   //
     352             :   //         C1     C2
     353             :   //          .     .
     354             :   //          |     |
     355             :   //         Merge(merge)  <-----------------+
     356             :   //           ^    ^                        |
     357             :   //  V1  V2   |    |         E1  E2         |
     358             :   //   .  .    |    +----+     .  .          |
     359             :   //   |  |    |         |     |  |          |
     360             :   //  Phi(callee)      EffectPhi(effect_phi) |
     361             :   //     ^                    ^              |
     362             :   //     |                    |              |
     363             :   //     +----+               |              |
     364             :   //     |    |               |              |
     365             :   //     |   StateValues      |              |
     366             :   //     |       ^            |              |
     367             :   //     +----+  |            |              |
     368             :   //     |    |  |            |              |
     369             :   //     |    FrameState      |              |
     370             :   //     |           ^        |              |
     371             :   //     |           |        |          +---+
     372             :   //     |           |        |          |   |
     373             :   //     +----+     Checkpoint(checkpoint)   |
     374             :   //     |    |           ^                  |
     375             :   //     |    StateValues |    +-------------+
     376             :   //     |        |       |    |
     377             :   //     +-----+  |       |    |
     378             :   //     |     |  |       |    |
     379             :   //     |     FrameState |    |
     380             :   //     |             ^  |    |
     381             :   //     +-----------+ |  |    |
     382             :   //                  Call(node)
     383             :   //                   |
     384             :   //                   |
     385             :   //                   .
     386             :   //
     387             :   // The {callee} here is a phi that merges the possible call targets, {node}
     388             :   // is the actual call that we will try to duplicate and connect to the
     389             :   // control that comes into {merge}. There can be a {checkpoint} between
     390             :   // the call and the calle phi.
     391             :   //
     392             :   // The idea is to get rid of the merge, effect phi and phi, then duplicate
     393             :   // the call (with all the frame states and such), and connect the duplicated
     394             :   // calls and states directly to the inputs of the ex-phi, ex-effect-phi and
     395             :   // ex-merge. The tricky part is to make sure that there is no interference
     396             :   // from the outside. In particular, there should not be any unaccounted uses
     397             :   // of the  phi, effect-phi and merge because we will remove them from
     398             :   // the graph.
     399             :   //
     400             :   //     V1              E1   C1  V2   E2               C2
     401             :   //     .                .    .  .    .                .
     402             :   //     |                |    |  |    |                |
     403             :   //     +----+           |    |  +----+                |
     404             :   //     |    |           |    |  |    |                |
     405             :   //     |   StateValues  |    |  |   StateValues       |
     406             :   //     |       ^        |    |  |       ^             |
     407             :   //     +----+  |        |    |  +----+  |             |
     408             :   //     |    |  |        |    |  |    |  |             |
     409             :   //     |    FrameState  |    |  |    FrameState       |
     410             :   //     |           ^    |    |  |           ^         |
     411             :   //     |           |    |    |  |           |         |
     412             :   //     |           |    |    |  |           |         |
     413             :   //     +----+     Checkpoint |  +----+     Checkpoint |
     414             :   //     |    |           ^    |  |    |           ^    |
     415             :   //     |    StateValues |    |  |    StateValues |    |
     416             :   //     |        |       |    |  |        |       |    |
     417             :   //     +-----+  |       |    |  +-----+  |       |    |
     418             :   //     |     |  |       |    |  |     |  |       |    |
     419             :   //     |     FrameState |    |  |     FrameState |    |
     420             :   //     |              ^ |    |  |              ^ |    |
     421             :   //     +-------------+| |    |  +-------------+| |    |
     422             :   //                   Call----+                Call----+
     423             :   //                     |                       |
     424             :   //                     +-------+  +------------+
     425             :   //                             |  |
     426             :   //                             Merge
     427             :   //                             EffectPhi
     428             :   //                             Phi
     429             :   //                              |
     430             :   //                             ...
     431             : 
     432             :   // If there is a control node between the callee computation
     433             :   // and the call, bail out.
     434          77 :   Node* merge = NodeProperties::GetControlInput(callee);
     435          77 :   if (NodeProperties::GetControlInput(node) != merge) return false;
     436             : 
     437             :   // If there is a non-checkpoint effect node between the callee computation
     438             :   // and the call, bail out. We will drop any checkpoint between the call and
     439             :   // the callee phi because the callee computation should have its own
     440             :   // checkpoint that the call can fall back to.
     441         204 :   Node* checkpoint = nullptr;
     442         154 :   Node* effect = NodeProperties::GetEffectInput(node);
     443          77 :   if (effect->opcode() == IrOpcode::kCheckpoint) {
     444             :     checkpoint = effect;
     445          77 :     if (NodeProperties::GetControlInput(checkpoint) != merge) return false;
     446          77 :     effect = NodeProperties::GetEffectInput(effect);
     447             :   }
     448          77 :   if (effect->opcode() != IrOpcode::kEffectPhi) return false;
     449          77 :   if (NodeProperties::GetControlInput(effect) != merge) return false;
     450             :   Node* effect_phi = effect;
     451             : 
     452             :   // The effect phi, the callee, the call and the checkpoint must be the only
     453             :   // users of the merge.
     454         371 :   for (Node* merge_use : merge->uses()) {
     455         462 :     if (merge_use != effect_phi && merge_use != callee && merge_use != node &&
     456         161 :         merge_use != checkpoint) {
     457             :       return false;
     458             :     }
     459             :   }
     460             : 
     461             :   // The effect phi must be only used by the checkpoint or the call.
     462         140 :   for (Node* effect_phi_use : effect_phi->uses()) {
     463          70 :     if (effect_phi_use != node && effect_phi_use != checkpoint) return false;
     464             :   }
     465             : 
     466             :   // We must replace the callee phi with the appropriate constant in
     467             :   // the entire subgraph reachable by inputs from the call (terminating
     468             :   // at phis and merges). Since we do not want to walk (and later duplicate)
     469             :   // the subgraph here, we limit the possible uses to this set:
     470             :   //
     471             :   // 1. In the call (as a target).
     472             :   // 2. The checkpoint between the call and the callee computation merge.
     473             :   // 3. The lazy deoptimization frame state.
     474             :   //
     475             :   // This corresponds to the most common pattern, where the function is
     476             :   // called with only local variables or constants as arguments.
     477             :   //
     478             :   // To check the uses, we first collect all the occurrences of callee in 1, 2
     479             :   // and 3, and then we check that all uses of callee are in the collected
     480             :   // occurrences. If there is an unaccounted use, we do not try to rewire
     481             :   // the control flow.
     482             :   //
     483             :   // Note: With CFG, this would be much easier and more robust - we would just
     484             :   // duplicate all the nodes between the merge and the call, replacing all
     485             :   // occurrences of the {callee} phi with the appropriate constant.
     486             : 
     487             :   // First compute the set of uses that are only reachable from 2 and 3.
     488             :   const size_t kMaxUses = 8;
     489             :   NodeAndIndex replaceable_uses[kMaxUses];
     490          70 :   size_t replaceable_uses_count = 0;
     491             : 
     492             :   // Collect the uses to check case 2.
     493             :   Node* checkpoint_state = nullptr;
     494          70 :   if (checkpoint) {
     495             :     checkpoint_state = checkpoint->InputAt(0);
     496          70 :     if (!CollectFrameStateUniqueUses(callee, checkpoint_state, replaceable_uses,
     497          70 :                                      &replaceable_uses_count, kMaxUses)) {
     498             :       return false;
     499             :     }
     500             :   }
     501             : 
     502             :   // Collect the uses to check case 3.
     503          70 :   Node* frame_state = NodeProperties::GetFrameStateInput(node);
     504          70 :   if (!CollectFrameStateUniqueUses(callee, frame_state, replaceable_uses,
     505          70 :                                    &replaceable_uses_count, kMaxUses)) {
     506             :     return false;
     507             :   }
     508             : 
     509             :   // Bail out if there is a use of {callee} that is not reachable from 1, 2
     510             :   // and 3.
     511         210 :   for (Edge edge : callee->use_edges()) {
     512             :     // Case 1 (use by the call as a target).
     513         140 :     if (edge.from() == node && edge.index() == 0) continue;
     514             :     // Case 2 and 3 - used in checkpoint and/or lazy deopt frame states.
     515             :     bool found = false;
     516           0 :     for (size_t i = 0; i < replaceable_uses_count; i++) {
     517         210 :       if (replaceable_uses[i].node == edge.from() &&
     518          70 :           replaceable_uses[i].index == edge.index()) {
     519             :         found = true;
     520             :         break;
     521             :       }
     522             :     }
     523          70 :     if (!found) return false;
     524             :   }
     525             : 
     526             :   // Clone the call and the framestate, including the uniquely reachable
     527             :   // state values, making sure that we replace the phi with the constant.
     528         274 :   for (int i = 0; i < num_calls; ++i) {
     529             :     // Clone the calls for each branch.
     530             :     // We need to specialize the calls to the correct target, effect, and
     531             :     // control. We also need to duplicate the checkpoint and the lazy
     532             :     // frame state, and change all the uses of the callee to the constant
     533             :     // callee.
     534             :     Node* target = callee->InputAt(i);
     535             :     Node* effect = effect_phi->InputAt(i);
     536             :     Node* control = merge->InputAt(i);
     537             : 
     538         204 :     if (checkpoint) {
     539             :       // Duplicate the checkpoint.
     540             :       Node* new_checkpoint_state = DuplicateFrameStateAndRename(
     541             :           checkpoint_state, callee, target,
     542         204 :           (i == num_calls - 1) ? kChangeInPlace : kCloneState);
     543             :       effect = graph()->NewNode(checkpoint->op(), new_checkpoint_state, effect,
     544             :                                 control);
     545             :     }
     546             : 
     547             :     // Duplicate the call.
     548             :     Node* new_lazy_frame_state = DuplicateFrameStateAndRename(
     549             :         frame_state, callee, target,
     550         204 :         (i == num_calls - 1) ? kChangeInPlace : kCloneState);
     551         204 :     inputs[0] = target;
     552         204 :     inputs[input_count - 3] = new_lazy_frame_state;
     553         204 :     inputs[input_count - 2] = effect;
     554         204 :     inputs[input_count - 1] = control;
     555         204 :     calls[i] = if_successes[i] =
     556         204 :         graph()->NewNode(node->op(), input_count, inputs);
     557             :   }
     558             : 
     559             :   // Mark the control inputs dead, so that we can kill the merge.
     560          70 :   node->ReplaceInput(input_count - 1, jsgraph()->Dead());
     561          70 :   callee->ReplaceInput(num_calls, jsgraph()->Dead());
     562          70 :   effect_phi->ReplaceInput(num_calls, jsgraph()->Dead());
     563          70 :   if (checkpoint) {
     564          70 :     checkpoint->ReplaceInput(2, jsgraph()->Dead());
     565             :   }
     566             : 
     567          70 :   merge->Kill();
     568          70 :   return true;
     569             : }
     570             : 
     571         105 : void JSInliningHeuristic::CreateOrReuseDispatch(Node* node, Node* callee,
     572             :                                                 Candidate const& candidate,
     573             :                                                 Node** if_successes,
     574             :                                                 Node** calls, Node** inputs,
     575          14 :                                                 int input_count) {
     576          77 :   if (TryReuseDispatch(node, callee, candidate, if_successes, calls, inputs,
     577             :                        input_count)) {
     578          77 :     return;
     579             :   }
     580             : 
     581           7 :   Node* fallthrough_control = NodeProperties::GetControlInput(node);
     582           7 :   int const num_calls = candidate.num_functions;
     583             : 
     584             :   // Create the appropriate control flow to dispatch to the cloned calls.
     585          21 :   for (int i = 0; i < num_calls; ++i) {
     586             :     // TODO(2206): Make comparison be based on underlying SharedFunctionInfo
     587             :     // instead of the target JSFunction reference directly.
     588          14 :     Node* target = jsgraph()->HeapConstant(candidate.functions[i]);
     589          14 :     if (i != (num_calls - 1)) {
     590             :       Node* check =
     591           7 :           graph()->NewNode(simplified()->ReferenceEqual(), callee, target);
     592             :       Node* branch =
     593           7 :           graph()->NewNode(common()->Branch(), check, fallthrough_control);
     594           7 :       fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
     595          21 :       if_successes[i] = graph()->NewNode(common()->IfTrue(), branch);
     596             :     } else {
     597           7 :       if_successes[i] = fallthrough_control;
     598             :     }
     599             : 
     600             :     // Clone the calls for each branch.
     601             :     // The first input to the call is the actual target (which we specialize
     602             :     // to the known {target}); the last input is the control dependency.
     603             :     // We also specialize the new.target of JSConstruct {node}s if it refers
     604             :     // to the same node as the {node}'s target input, so that we can later
     605             :     // properly inline the JSCreate operations.
     606          14 :     if (node->opcode() == IrOpcode::kJSConstruct && inputs[0] == inputs[1]) {
     607           0 :       inputs[1] = target;
     608             :     }
     609          14 :     inputs[0] = target;
     610          14 :     inputs[input_count - 1] = if_successes[i];
     611          14 :     calls[i] = if_successes[i] =
     612          14 :         graph()->NewNode(node->op(), input_count, inputs);
     613             :   }
     614             : }
     615             : 
     616       62140 : Reduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate,
     617             :                                                bool small_function) {
     618       62140 :   int const num_calls = candidate.num_functions;
     619       62140 :   Node* const node = candidate.node;
     620       62140 :   if (num_calls == 1) {
     621             :     Handle<SharedFunctionInfo> shared =
     622             :         candidate.functions[0].is_null()
     623             :             ? candidate.shared_info
     624       62063 :             : handle(candidate.functions[0]->shared());
     625       62063 :     Reduction const reduction = inliner_.ReduceJSCall(node);
     626       62063 :     if (reduction.Changed()) {
     627       61310 :       cumulative_count_ += shared->bytecode_array()->length();
     628             :     }
     629       62063 :     return reduction;
     630             :   }
     631             : 
     632             :   // Expand the JSCall/JSConstruct node to a subgraph first if
     633             :   // we have multiple known target functions.
     634             :   DCHECK_LT(1, num_calls);
     635             :   Node* calls[kMaxCallPolymorphism + 1];
     636             :   Node* if_successes[kMaxCallPolymorphism];
     637          77 :   Node* callee = NodeProperties::GetValueInput(node, 0);
     638             : 
     639             :   // Setup the inputs for the cloned call nodes.
     640             :   int const input_count = node->InputCount();
     641         154 :   Node** inputs = graph()->zone()->NewArray<Node*>(input_count);
     642         587 :   for (int i = 0; i < input_count; ++i) {
     643        1020 :     inputs[i] = node->InputAt(i);
     644             :   }
     645             : 
     646             :   // Create the appropriate control flow to dispatch to the cloned calls.
     647             :   CreateOrReuseDispatch(node, callee, candidate, if_successes, calls, inputs,
     648          77 :                         input_count);
     649             : 
     650             :   // Check if we have an exception projection for the call {node}.
     651          77 :   Node* if_exception = nullptr;
     652          77 :   if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
     653             :     Node* if_exceptions[kMaxCallPolymorphism + 1];
     654           0 :     for (int i = 0; i < num_calls; ++i) {
     655           0 :       if_successes[i] = graph()->NewNode(common()->IfSuccess(), calls[i]);
     656             :       if_exceptions[i] =
     657           0 :           graph()->NewNode(common()->IfException(), calls[i], calls[i]);
     658             :     }
     659             : 
     660             :     // Morph the {if_exception} projection into a join.
     661             :     Node* exception_control =
     662           0 :         graph()->NewNode(common()->Merge(num_calls), num_calls, if_exceptions);
     663           0 :     if_exceptions[num_calls] = exception_control;
     664             :     Node* exception_effect = graph()->NewNode(common()->EffectPhi(num_calls),
     665           0 :                                               num_calls + 1, if_exceptions);
     666             :     Node* exception_value = graph()->NewNode(
     667             :         common()->Phi(MachineRepresentation::kTagged, num_calls), num_calls + 1,
     668           0 :         if_exceptions);
     669             :     ReplaceWithValue(if_exception, exception_value, exception_effect,
     670          77 :                      exception_control);
     671             :   }
     672             : 
     673             :   // Morph the original call site into a join of the dispatched call sites.
     674             :   Node* control =
     675         154 :       graph()->NewNode(common()->Merge(num_calls), num_calls, if_successes);
     676          77 :   calls[num_calls] = control;
     677             :   Node* effect =
     678         231 :       graph()->NewNode(common()->EffectPhi(num_calls), num_calls + 1, calls);
     679             :   Node* value =
     680             :       graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, num_calls),
     681         154 :                        num_calls + 1, calls);
     682             :   ReplaceWithValue(node, value, effect, control);
     683             : 
     684             :   // Inline the individual, cloned call sites.
     685         295 :   for (int i = 0; i < num_calls; ++i) {
     686         218 :     Handle<JSFunction> function = candidate.functions[i];
     687         218 :     Node* node = calls[i];
     688         218 :     if (small_function ||
     689           0 :         (candidate.can_inline_function[i] &&
     690           0 :          cumulative_count_ < FLAG_max_inlined_bytecode_size_cumulative)) {
     691         218 :       Reduction const reduction = inliner_.ReduceJSCall(node);
     692         218 :       if (reduction.Changed()) {
     693             :         // Killing the call node is not strictly necessary, but it is safer to
     694             :         // make sure we do not resurrect the node.
     695         218 :         node->Kill();
     696         218 :         cumulative_count_ += function->shared()->bytecode_array()->length();
     697             :       }
     698             :     }
     699             :   }
     700             : 
     701             :   return Replace(value);
     702             : }
     703             : 
     704      193790 : bool JSInliningHeuristic::CandidateCompare::operator()(
     705             :     const Candidate& left, const Candidate& right) const {
     706      193790 :   if (right.frequency.IsUnknown()) {
     707         958 :     if (left.frequency.IsUnknown()) {
     708             :       // If left and right are both unknown then the ordering is indeterminate,
     709             :       // which breaks strict weak ordering requirements, so we fall back to the
     710             :       // node id as a tie breaker.
     711      276966 :       return left.node->id() > right.node->id();
     712             :     }
     713             :     return true;
     714      192832 :   } else if (left.frequency.IsUnknown()) {
     715             :     return false;
     716      192832 :   } else if (left.frequency.value() > right.frequency.value()) {
     717             :     return true;
     718      142689 :   } else if (left.frequency.value() < right.frequency.value()) {
     719             :     return false;
     720             :   } else {
     721      411138 :     return left.node->id() > right.node->id();
     722             :   }
     723             : }
     724             : 
     725           0 : void JSInliningHeuristic::PrintCandidates() {
     726           0 :   OFStream os(stdout);
     727           0 :   os << "Candidates for inlining (size=" << candidates_.size() << "):\n";
     728           0 :   for (const Candidate& candidate : candidates_) {
     729           0 :     os << "  #" << candidate.node->id() << ":"
     730           0 :        << candidate.node->op()->mnemonic()
     731           0 :        << ", frequency: " << candidate.frequency << std::endl;
     732           0 :     for (int i = 0; i < candidate.num_functions; ++i) {
     733             :       Handle<SharedFunctionInfo> shared =
     734           0 :           candidate.functions[i].is_null()
     735             :               ? candidate.shared_info
     736           0 :               : handle(candidate.functions[i]->shared());
     737             :       PrintF("  - size:%d, name: %s\n", shared->bytecode_array()->length(),
     738           0 :              shared->DebugName()->ToCString().get());
     739             :     }
     740           0 :   }
     741           0 : }
     742             : 
     743        1026 : Graph* JSInliningHeuristic::graph() const { return jsgraph()->graph(); }
     744             : 
     745         252 : CommonOperatorBuilder* JSInliningHeuristic::common() const {
     746         252 :   return jsgraph()->common();
     747             : }
     748             : 
     749           7 : SimplifiedOperatorBuilder* JSInliningHeuristic::simplified() const {
     750           7 :   return jsgraph()->simplified();
     751             : }
     752             : 
     753             : #undef TRACE
     754             : 
     755             : }  // namespace compiler
     756             : }  // namespace internal
     757             : }  // namespace v8

Generated by: LCOV version 1.10