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