11cb0ef41Sopenharmony_ci// Copyright 2015 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/compiler/js-inlining-heuristic.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include "src/codegen/optimized-compilation-info.h"
81cb0ef41Sopenharmony_ci#include "src/compiler/common-operator.h"
91cb0ef41Sopenharmony_ci#include "src/compiler/compiler-source-position-table.h"
101cb0ef41Sopenharmony_ci#include "src/compiler/js-heap-broker.h"
111cb0ef41Sopenharmony_ci#include "src/compiler/node-matchers.h"
121cb0ef41Sopenharmony_ci#include "src/compiler/simplified-operator.h"
131cb0ef41Sopenharmony_ci#include "src/objects/objects-inl.h"
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_cinamespace v8 {
161cb0ef41Sopenharmony_cinamespace internal {
171cb0ef41Sopenharmony_cinamespace compiler {
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci#define TRACE(...)                                                             \
201cb0ef41Sopenharmony_ci  do {                                                                         \
211cb0ef41Sopenharmony_ci    if (FLAG_trace_turbo_inlining) StdoutStream{} << __VA_ARGS__ << std::endl; \
221cb0ef41Sopenharmony_ci  } while (false)
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_cinamespace {
251cb0ef41Sopenharmony_cibool IsSmall(int const size) {
261cb0ef41Sopenharmony_ci  return size <= FLAG_max_inlined_bytecode_size_small;
271cb0ef41Sopenharmony_ci}
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_cibool CanConsiderForInlining(JSHeapBroker* broker,
301cb0ef41Sopenharmony_ci                            FeedbackCellRef const& feedback_cell) {
311cb0ef41Sopenharmony_ci  base::Optional<FeedbackVectorRef> feedback_vector =
321cb0ef41Sopenharmony_ci      feedback_cell.feedback_vector();
331cb0ef41Sopenharmony_ci  if (!feedback_vector.has_value()) {
341cb0ef41Sopenharmony_ci    TRACE("Cannot consider " << feedback_cell
351cb0ef41Sopenharmony_ci                             << " for inlining (no feedback vector)");
361cb0ef41Sopenharmony_ci    return false;
371cb0ef41Sopenharmony_ci  }
381cb0ef41Sopenharmony_ci  SharedFunctionInfoRef shared = feedback_vector->shared_function_info();
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ci  if (!shared.HasBytecodeArray()) {
411cb0ef41Sopenharmony_ci    TRACE("Cannot consider " << shared << " for inlining (no bytecode)");
421cb0ef41Sopenharmony_ci    return false;
431cb0ef41Sopenharmony_ci  }
441cb0ef41Sopenharmony_ci  // Ensure we have a persistent handle to the bytecode in order to avoid
451cb0ef41Sopenharmony_ci  // flushing it during the remaining compilation.
461cb0ef41Sopenharmony_ci  shared.GetBytecodeArray();
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci  // Read feedback vector again in case it got flushed before we were able to
491cb0ef41Sopenharmony_ci  // prevent flushing above.
501cb0ef41Sopenharmony_ci  base::Optional<FeedbackVectorRef> feedback_vector_again =
511cb0ef41Sopenharmony_ci      feedback_cell.feedback_vector();
521cb0ef41Sopenharmony_ci  if (!feedback_vector_again.has_value()) {
531cb0ef41Sopenharmony_ci    TRACE("Cannot consider " << shared << " for inlining (no feedback vector)");
541cb0ef41Sopenharmony_ci    return false;
551cb0ef41Sopenharmony_ci  }
561cb0ef41Sopenharmony_ci  if (!feedback_vector_again->equals(*feedback_vector)) {
571cb0ef41Sopenharmony_ci    // The new feedback vector likely contains lots of uninitialized slots, so
581cb0ef41Sopenharmony_ci    // it doesn't make much sense to inline this function now.
591cb0ef41Sopenharmony_ci    TRACE("Not considering " << shared
601cb0ef41Sopenharmony_ci                             << " for inlining (feedback vector changed)");
611cb0ef41Sopenharmony_ci    return false;
621cb0ef41Sopenharmony_ci  }
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci  SharedFunctionInfo::Inlineability inlineability = shared.GetInlineability();
651cb0ef41Sopenharmony_ci  if (inlineability != SharedFunctionInfo::kIsInlineable) {
661cb0ef41Sopenharmony_ci    TRACE("Cannot consider "
671cb0ef41Sopenharmony_ci          << shared << " for inlining (reason: " << inlineability << ")");
681cb0ef41Sopenharmony_ci    return false;
691cb0ef41Sopenharmony_ci  }
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci  TRACE("Considering " << shared << " for inlining with " << *feedback_vector);
721cb0ef41Sopenharmony_ci  return true;
731cb0ef41Sopenharmony_ci}
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_cibool CanConsiderForInlining(JSHeapBroker* broker,
761cb0ef41Sopenharmony_ci                            JSFunctionRef const& function) {
771cb0ef41Sopenharmony_ci  FeedbackCellRef feedback_cell =
781cb0ef41Sopenharmony_ci      function.raw_feedback_cell(broker->dependencies());
791cb0ef41Sopenharmony_ci  bool const result = CanConsiderForInlining(broker, feedback_cell);
801cb0ef41Sopenharmony_ci  if (result) {
811cb0ef41Sopenharmony_ci    CHECK(
821cb0ef41Sopenharmony_ci        function.shared().equals(feedback_cell.shared_function_info().value()));
831cb0ef41Sopenharmony_ci  }
841cb0ef41Sopenharmony_ci  return result;
851cb0ef41Sopenharmony_ci}
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci}  // namespace
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ciJSInliningHeuristic::Candidate JSInliningHeuristic::CollectFunctions(
901cb0ef41Sopenharmony_ci    Node* node, int functions_size) {
911cb0ef41Sopenharmony_ci  DCHECK_NE(0, functions_size);
921cb0ef41Sopenharmony_ci  Node* callee = node->InputAt(0);
931cb0ef41Sopenharmony_ci  Candidate out;
941cb0ef41Sopenharmony_ci  out.node = node;
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci  HeapObjectMatcher m(callee);
971cb0ef41Sopenharmony_ci  if (m.HasResolvedValue() && m.Ref(broker()).IsJSFunction()) {
981cb0ef41Sopenharmony_ci    JSFunctionRef function = m.Ref(broker()).AsJSFunction();
991cb0ef41Sopenharmony_ci    out.functions[0] = function;
1001cb0ef41Sopenharmony_ci    if (CanConsiderForInlining(broker(), function)) {
1011cb0ef41Sopenharmony_ci      out.bytecode[0] = function.shared().GetBytecodeArray();
1021cb0ef41Sopenharmony_ci      out.num_functions = 1;
1031cb0ef41Sopenharmony_ci      return out;
1041cb0ef41Sopenharmony_ci    }
1051cb0ef41Sopenharmony_ci  }
1061cb0ef41Sopenharmony_ci  if (m.IsPhi()) {
1071cb0ef41Sopenharmony_ci    int const value_input_count = m.node()->op()->ValueInputCount();
1081cb0ef41Sopenharmony_ci    if (value_input_count > functions_size) {
1091cb0ef41Sopenharmony_ci      out.num_functions = 0;
1101cb0ef41Sopenharmony_ci      return out;
1111cb0ef41Sopenharmony_ci    }
1121cb0ef41Sopenharmony_ci    for (int n = 0; n < value_input_count; ++n) {
1131cb0ef41Sopenharmony_ci      HeapObjectMatcher m2(callee->InputAt(n));
1141cb0ef41Sopenharmony_ci      if (!m2.HasResolvedValue() || !m2.Ref(broker()).IsJSFunction()) {
1151cb0ef41Sopenharmony_ci        out.num_functions = 0;
1161cb0ef41Sopenharmony_ci        return out;
1171cb0ef41Sopenharmony_ci      }
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci      out.functions[n] = m2.Ref(broker()).AsJSFunction();
1201cb0ef41Sopenharmony_ci      JSFunctionRef function = out.functions[n].value();
1211cb0ef41Sopenharmony_ci      if (CanConsiderForInlining(broker(), function)) {
1221cb0ef41Sopenharmony_ci        out.bytecode[n] = function.shared().GetBytecodeArray();
1231cb0ef41Sopenharmony_ci      }
1241cb0ef41Sopenharmony_ci    }
1251cb0ef41Sopenharmony_ci    out.num_functions = value_input_count;
1261cb0ef41Sopenharmony_ci    return out;
1271cb0ef41Sopenharmony_ci  }
1281cb0ef41Sopenharmony_ci  if (m.IsCheckClosure()) {
1291cb0ef41Sopenharmony_ci    DCHECK(!out.functions[0].has_value());
1301cb0ef41Sopenharmony_ci    FeedbackCellRef feedback_cell = MakeRef(broker(), FeedbackCellOf(m.op()));
1311cb0ef41Sopenharmony_ci    if (CanConsiderForInlining(broker(), feedback_cell)) {
1321cb0ef41Sopenharmony_ci      out.shared_info = feedback_cell.shared_function_info().value();
1331cb0ef41Sopenharmony_ci      out.bytecode[0] = out.shared_info->GetBytecodeArray();
1341cb0ef41Sopenharmony_ci    }
1351cb0ef41Sopenharmony_ci    out.num_functions = 1;
1361cb0ef41Sopenharmony_ci    return out;
1371cb0ef41Sopenharmony_ci  }
1381cb0ef41Sopenharmony_ci  if (m.IsJSCreateClosure()) {
1391cb0ef41Sopenharmony_ci    DCHECK(!out.functions[0].has_value());
1401cb0ef41Sopenharmony_ci    JSCreateClosureNode n(callee);
1411cb0ef41Sopenharmony_ci    FeedbackCellRef feedback_cell = n.GetFeedbackCellRefChecked(broker());
1421cb0ef41Sopenharmony_ci    if (CanConsiderForInlining(broker(), feedback_cell)) {
1431cb0ef41Sopenharmony_ci      out.shared_info = feedback_cell.shared_function_info().value();
1441cb0ef41Sopenharmony_ci      out.bytecode[0] = out.shared_info->GetBytecodeArray();
1451cb0ef41Sopenharmony_ci      CHECK(out.shared_info->equals(n.Parameters().shared_info(broker())));
1461cb0ef41Sopenharmony_ci    }
1471cb0ef41Sopenharmony_ci    out.num_functions = 1;
1481cb0ef41Sopenharmony_ci    return out;
1491cb0ef41Sopenharmony_ci  }
1501cb0ef41Sopenharmony_ci  out.num_functions = 0;
1511cb0ef41Sopenharmony_ci  return out;
1521cb0ef41Sopenharmony_ci}
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_ciReduction JSInliningHeuristic::Reduce(Node* node) {
1551cb0ef41Sopenharmony_ci#if V8_ENABLE_WEBASSEMBLY
1561cb0ef41Sopenharmony_ci  if (mode() == kWasmOnly) {
1571cb0ef41Sopenharmony_ci    if (node->opcode() == IrOpcode::kJSWasmCall) {
1581cb0ef41Sopenharmony_ci      return inliner_.ReduceJSWasmCall(node);
1591cb0ef41Sopenharmony_ci    }
1601cb0ef41Sopenharmony_ci    return NoChange();
1611cb0ef41Sopenharmony_ci  }
1621cb0ef41Sopenharmony_ci#endif  // V8_ENABLE_WEBASSEMBLY
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci  DCHECK_EQ(mode(), kJSOnly);
1651cb0ef41Sopenharmony_ci  if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci  if (total_inlined_bytecode_size_ >= max_inlined_bytecode_size_absolute_) {
1681cb0ef41Sopenharmony_ci    return NoChange();
1691cb0ef41Sopenharmony_ci  }
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_ci  // Check if we already saw that {node} before, and if so, just skip it.
1721cb0ef41Sopenharmony_ci  if (seen_.find(node->id()) != seen_.end()) return NoChange();
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci  // Check if the {node} is an appropriate candidate for inlining.
1751cb0ef41Sopenharmony_ci  Candidate candidate = CollectFunctions(node, kMaxCallPolymorphism);
1761cb0ef41Sopenharmony_ci  if (candidate.num_functions == 0) {
1771cb0ef41Sopenharmony_ci    return NoChange();
1781cb0ef41Sopenharmony_ci  } else if (candidate.num_functions > 1 && !FLAG_polymorphic_inlining) {
1791cb0ef41Sopenharmony_ci    TRACE("Not considering call site #"
1801cb0ef41Sopenharmony_ci          << node->id() << ":" << node->op()->mnemonic()
1811cb0ef41Sopenharmony_ci          << ", because polymorphic inlining is disabled");
1821cb0ef41Sopenharmony_ci    return NoChange();
1831cb0ef41Sopenharmony_ci  }
1841cb0ef41Sopenharmony_ci
1851cb0ef41Sopenharmony_ci  bool can_inline_candidate = false, candidate_is_small = true;
1861cb0ef41Sopenharmony_ci  candidate.total_size = 0;
1871cb0ef41Sopenharmony_ci  FrameState frame_state{NodeProperties::GetFrameStateInput(node)};
1881cb0ef41Sopenharmony_ci  FrameStateInfo const& frame_info = frame_state.frame_state_info();
1891cb0ef41Sopenharmony_ci  Handle<SharedFunctionInfo> frame_shared_info;
1901cb0ef41Sopenharmony_ci  for (int i = 0; i < candidate.num_functions; ++i) {
1911cb0ef41Sopenharmony_ci    if (!candidate.bytecode[i].has_value()) {
1921cb0ef41Sopenharmony_ci      candidate.can_inline_function[i] = false;
1931cb0ef41Sopenharmony_ci      continue;
1941cb0ef41Sopenharmony_ci    }
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_ci    SharedFunctionInfoRef shared = candidate.functions[i].has_value()
1971cb0ef41Sopenharmony_ci                                       ? candidate.functions[i].value().shared()
1981cb0ef41Sopenharmony_ci                                       : candidate.shared_info.value();
1991cb0ef41Sopenharmony_ci    candidate.can_inline_function[i] = candidate.bytecode[i].has_value();
2001cb0ef41Sopenharmony_ci    // Because of concurrent optimization, optimization of the inlining
2011cb0ef41Sopenharmony_ci    // candidate could have been disabled meanwhile.
2021cb0ef41Sopenharmony_ci    // JSInliner will check this again and not actually inline the function in
2031cb0ef41Sopenharmony_ci    // this case.
2041cb0ef41Sopenharmony_ci    CHECK_IMPLIES(candidate.can_inline_function[i],
2051cb0ef41Sopenharmony_ci                  shared.IsInlineable() ||
2061cb0ef41Sopenharmony_ci                      shared.GetInlineability() ==
2071cb0ef41Sopenharmony_ci                          SharedFunctionInfo::kHasOptimizationDisabled);
2081cb0ef41Sopenharmony_ci    // Do not allow direct recursion i.e. f() -> f(). We still allow indirect
2091cb0ef41Sopenharmony_ci    // recursion like f() -> g() -> f(). The indirect recursion is helpful in
2101cb0ef41Sopenharmony_ci    // cases where f() is a small dispatch function that calls the appropriate
2111cb0ef41Sopenharmony_ci    // function. In the case of direct recursion, we only have some static
2121cb0ef41Sopenharmony_ci    // information for the first level of inlining and it may not be that useful
2131cb0ef41Sopenharmony_ci    // to just inline one level in recursive calls. In some cases like tail
2141cb0ef41Sopenharmony_ci    // recursion we may benefit from recursive inlining, if we have additional
2151cb0ef41Sopenharmony_ci    // analysis that converts them to iterative implementations. Though it is
2161cb0ef41Sopenharmony_ci    // not obvious if such an analysis is needed.
2171cb0ef41Sopenharmony_ci    if (frame_info.shared_info().ToHandle(&frame_shared_info) &&
2181cb0ef41Sopenharmony_ci        frame_shared_info.equals(shared.object())) {
2191cb0ef41Sopenharmony_ci      TRACE("Not considering call site #" << node->id() << ":"
2201cb0ef41Sopenharmony_ci                                          << node->op()->mnemonic()
2211cb0ef41Sopenharmony_ci                                          << ", because of recursive inlining");
2221cb0ef41Sopenharmony_ci      candidate.can_inline_function[i] = false;
2231cb0ef41Sopenharmony_ci    }
2241cb0ef41Sopenharmony_ci    if (candidate.can_inline_function[i]) {
2251cb0ef41Sopenharmony_ci      can_inline_candidate = true;
2261cb0ef41Sopenharmony_ci      BytecodeArrayRef bytecode = candidate.bytecode[i].value();
2271cb0ef41Sopenharmony_ci      candidate.total_size += bytecode.length();
2281cb0ef41Sopenharmony_ci      unsigned inlined_bytecode_size = 0;
2291cb0ef41Sopenharmony_ci      if (candidate.functions[i].has_value()) {
2301cb0ef41Sopenharmony_ci        JSFunctionRef function = candidate.functions[i].value();
2311cb0ef41Sopenharmony_ci        inlined_bytecode_size = function.code().GetInlinedBytecodeSize();
2321cb0ef41Sopenharmony_ci        candidate.total_size += inlined_bytecode_size;
2331cb0ef41Sopenharmony_ci      }
2341cb0ef41Sopenharmony_ci      candidate_is_small = candidate_is_small &&
2351cb0ef41Sopenharmony_ci                           IsSmall(bytecode.length() + inlined_bytecode_size);
2361cb0ef41Sopenharmony_ci    }
2371cb0ef41Sopenharmony_ci  }
2381cb0ef41Sopenharmony_ci  if (!can_inline_candidate) return NoChange();
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci  // Gather feedback on how often this call site has been hit before.
2411cb0ef41Sopenharmony_ci  if (node->opcode() == IrOpcode::kJSCall) {
2421cb0ef41Sopenharmony_ci    CallParameters const p = CallParametersOf(node->op());
2431cb0ef41Sopenharmony_ci    candidate.frequency = p.frequency();
2441cb0ef41Sopenharmony_ci  } else {
2451cb0ef41Sopenharmony_ci    ConstructParameters const p = ConstructParametersOf(node->op());
2461cb0ef41Sopenharmony_ci    candidate.frequency = p.frequency();
2471cb0ef41Sopenharmony_ci  }
2481cb0ef41Sopenharmony_ci
2491cb0ef41Sopenharmony_ci  // Don't consider a {candidate} whose frequency is below the
2501cb0ef41Sopenharmony_ci  // threshold, i.e. a call site that is only hit once every N
2511cb0ef41Sopenharmony_ci  // invocations of the caller.
2521cb0ef41Sopenharmony_ci  if (candidate.frequency.IsKnown() &&
2531cb0ef41Sopenharmony_ci      candidate.frequency.value() < FLAG_min_inlining_frequency) {
2541cb0ef41Sopenharmony_ci    return NoChange();
2551cb0ef41Sopenharmony_ci  }
2561cb0ef41Sopenharmony_ci
2571cb0ef41Sopenharmony_ci  // Found a candidate. Insert it into the set of seen nodes s.t. we don't
2581cb0ef41Sopenharmony_ci  // revisit in the future. Note this insertion happens here and not earlier in
2591cb0ef41Sopenharmony_ci  // order to make inlining decisions order-independent. A node may not be a
2601cb0ef41Sopenharmony_ci  // candidate when first seen, but later reductions may turn it into a valid
2611cb0ef41Sopenharmony_ci  // candidate. In that case, the node should be revisited by
2621cb0ef41Sopenharmony_ci  // JSInliningHeuristic.
2631cb0ef41Sopenharmony_ci  seen_.insert(node->id());
2641cb0ef41Sopenharmony_ci
2651cb0ef41Sopenharmony_ci  // Forcibly inline small functions here. In the case of polymorphic inlining
2661cb0ef41Sopenharmony_ci  // candidate_is_small is set only when all functions are small.
2671cb0ef41Sopenharmony_ci  if (candidate_is_small) {
2681cb0ef41Sopenharmony_ci    TRACE("Inlining small function(s) at call site #"
2691cb0ef41Sopenharmony_ci          << node->id() << ":" << node->op()->mnemonic());
2701cb0ef41Sopenharmony_ci    return InlineCandidate(candidate, true);
2711cb0ef41Sopenharmony_ci  }
2721cb0ef41Sopenharmony_ci
2731cb0ef41Sopenharmony_ci  // In the general case we remember the candidate for later.
2741cb0ef41Sopenharmony_ci  candidates_.insert(candidate);
2751cb0ef41Sopenharmony_ci  return NoChange();
2761cb0ef41Sopenharmony_ci}
2771cb0ef41Sopenharmony_ci
2781cb0ef41Sopenharmony_civoid JSInliningHeuristic::Finalize() {
2791cb0ef41Sopenharmony_ci  if (candidates_.empty()) return;  // Nothing to do without candidates.
2801cb0ef41Sopenharmony_ci  if (FLAG_trace_turbo_inlining) PrintCandidates();
2811cb0ef41Sopenharmony_ci
2821cb0ef41Sopenharmony_ci  // We inline at most one candidate in every iteration of the fixpoint.
2831cb0ef41Sopenharmony_ci  // This is to ensure that we don't consume the full inlining budget
2841cb0ef41Sopenharmony_ci  // on things that aren't called very often.
2851cb0ef41Sopenharmony_ci  // TODO(bmeurer): Use std::priority_queue instead of std::set here.
2861cb0ef41Sopenharmony_ci  while (!candidates_.empty()) {
2871cb0ef41Sopenharmony_ci    auto i = candidates_.begin();
2881cb0ef41Sopenharmony_ci    Candidate candidate = *i;
2891cb0ef41Sopenharmony_ci    candidates_.erase(i);
2901cb0ef41Sopenharmony_ci
2911cb0ef41Sopenharmony_ci    // Ignore this candidate if it's no longer valid.
2921cb0ef41Sopenharmony_ci    if (!IrOpcode::IsInlineeOpcode(candidate.node->opcode())) continue;
2931cb0ef41Sopenharmony_ci    if (candidate.node->IsDead()) continue;
2941cb0ef41Sopenharmony_ci
2951cb0ef41Sopenharmony_ci    // Make sure we have some extra budget left, so that any small functions
2961cb0ef41Sopenharmony_ci    // exposed by this function would be given a chance to inline.
2971cb0ef41Sopenharmony_ci    double size_of_candidate =
2981cb0ef41Sopenharmony_ci        candidate.total_size * FLAG_reserve_inline_budget_scale_factor;
2991cb0ef41Sopenharmony_ci    int total_size =
3001cb0ef41Sopenharmony_ci        total_inlined_bytecode_size_ + static_cast<int>(size_of_candidate);
3011cb0ef41Sopenharmony_ci    if (total_size > max_inlined_bytecode_size_cumulative_) {
3021cb0ef41Sopenharmony_ci      // Try if any smaller functions are available to inline.
3031cb0ef41Sopenharmony_ci      continue;
3041cb0ef41Sopenharmony_ci    }
3051cb0ef41Sopenharmony_ci
3061cb0ef41Sopenharmony_ci    Reduction const reduction = InlineCandidate(candidate, false);
3071cb0ef41Sopenharmony_ci    if (reduction.Changed()) return;
3081cb0ef41Sopenharmony_ci  }
3091cb0ef41Sopenharmony_ci}
3101cb0ef41Sopenharmony_ci
3111cb0ef41Sopenharmony_cinamespace {
3121cb0ef41Sopenharmony_ci
3131cb0ef41Sopenharmony_cistruct NodeAndIndex {
3141cb0ef41Sopenharmony_ci  Node* node;
3151cb0ef41Sopenharmony_ci  int index;
3161cb0ef41Sopenharmony_ci};
3171cb0ef41Sopenharmony_ci
3181cb0ef41Sopenharmony_cibool CollectStateValuesOwnedUses(Node* node, Node* state_values,
3191cb0ef41Sopenharmony_ci                                 NodeAndIndex* uses_buffer, size_t* use_count,
3201cb0ef41Sopenharmony_ci                                 size_t max_uses) {
3211cb0ef41Sopenharmony_ci  // Only accumulate states that are not shared with other users.
3221cb0ef41Sopenharmony_ci  if (state_values->UseCount() > 1) return true;
3231cb0ef41Sopenharmony_ci  for (int i = 0; i < state_values->InputCount(); i++) {
3241cb0ef41Sopenharmony_ci    Node* input = state_values->InputAt(i);
3251cb0ef41Sopenharmony_ci    if (input->opcode() == IrOpcode::kStateValues) {
3261cb0ef41Sopenharmony_ci      if (!CollectStateValuesOwnedUses(node, input, uses_buffer, use_count,
3271cb0ef41Sopenharmony_ci                                       max_uses)) {
3281cb0ef41Sopenharmony_ci        return false;
3291cb0ef41Sopenharmony_ci      }
3301cb0ef41Sopenharmony_ci    } else if (input == node) {
3311cb0ef41Sopenharmony_ci      if (*use_count >= max_uses) return false;
3321cb0ef41Sopenharmony_ci      uses_buffer[*use_count] = {state_values, i};
3331cb0ef41Sopenharmony_ci      (*use_count)++;
3341cb0ef41Sopenharmony_ci    }
3351cb0ef41Sopenharmony_ci  }
3361cb0ef41Sopenharmony_ci  return true;
3371cb0ef41Sopenharmony_ci}
3381cb0ef41Sopenharmony_ci
3391cb0ef41Sopenharmony_ci}  // namespace
3401cb0ef41Sopenharmony_ci
3411cb0ef41Sopenharmony_ciNode* JSInliningHeuristic::DuplicateStateValuesAndRename(Node* state_values,
3421cb0ef41Sopenharmony_ci                                                         Node* from, Node* to,
3431cb0ef41Sopenharmony_ci                                                         StateCloneMode mode) {
3441cb0ef41Sopenharmony_ci  // Only rename in states that are not shared with other users. This needs to
3451cb0ef41Sopenharmony_ci  // be in sync with the condition in {CollectStateValuesOwnedUses}.
3461cb0ef41Sopenharmony_ci  if (state_values->UseCount() > 1) return state_values;
3471cb0ef41Sopenharmony_ci  Node* copy = mode == kChangeInPlace ? state_values : nullptr;
3481cb0ef41Sopenharmony_ci  for (int i = 0; i < state_values->InputCount(); i++) {
3491cb0ef41Sopenharmony_ci    Node* input = state_values->InputAt(i);
3501cb0ef41Sopenharmony_ci    Node* processed;
3511cb0ef41Sopenharmony_ci    if (input->opcode() == IrOpcode::kStateValues) {
3521cb0ef41Sopenharmony_ci      processed = DuplicateStateValuesAndRename(input, from, to, mode);
3531cb0ef41Sopenharmony_ci    } else if (input == from) {
3541cb0ef41Sopenharmony_ci      processed = to;
3551cb0ef41Sopenharmony_ci    } else {
3561cb0ef41Sopenharmony_ci      processed = input;
3571cb0ef41Sopenharmony_ci    }
3581cb0ef41Sopenharmony_ci    if (processed != input) {
3591cb0ef41Sopenharmony_ci      if (!copy) {
3601cb0ef41Sopenharmony_ci        copy = graph()->CloneNode(state_values);
3611cb0ef41Sopenharmony_ci      }
3621cb0ef41Sopenharmony_ci      copy->ReplaceInput(i, processed);
3631cb0ef41Sopenharmony_ci    }
3641cb0ef41Sopenharmony_ci  }
3651cb0ef41Sopenharmony_ci  return copy ? copy : state_values;
3661cb0ef41Sopenharmony_ci}
3671cb0ef41Sopenharmony_ci
3681cb0ef41Sopenharmony_cinamespace {
3691cb0ef41Sopenharmony_ci
3701cb0ef41Sopenharmony_cibool CollectFrameStateUniqueUses(Node* node, FrameState frame_state,
3711cb0ef41Sopenharmony_ci                                 NodeAndIndex* uses_buffer, size_t* use_count,
3721cb0ef41Sopenharmony_ci                                 size_t max_uses) {
3731cb0ef41Sopenharmony_ci  // Only accumulate states that are not shared with other users.
3741cb0ef41Sopenharmony_ci  if (frame_state->UseCount() > 1) return true;
3751cb0ef41Sopenharmony_ci  if (frame_state.stack() == node) {
3761cb0ef41Sopenharmony_ci    if (*use_count >= max_uses) return false;
3771cb0ef41Sopenharmony_ci    uses_buffer[*use_count] = {frame_state, FrameState::kFrameStateStackInput};
3781cb0ef41Sopenharmony_ci    (*use_count)++;
3791cb0ef41Sopenharmony_ci  }
3801cb0ef41Sopenharmony_ci  if (!CollectStateValuesOwnedUses(node, frame_state.locals(), uses_buffer,
3811cb0ef41Sopenharmony_ci                                   use_count, max_uses)) {
3821cb0ef41Sopenharmony_ci    return false;
3831cb0ef41Sopenharmony_ci  }
3841cb0ef41Sopenharmony_ci  return true;
3851cb0ef41Sopenharmony_ci}
3861cb0ef41Sopenharmony_ci
3871cb0ef41Sopenharmony_ci}  // namespace
3881cb0ef41Sopenharmony_ci
3891cb0ef41Sopenharmony_ciFrameState JSInliningHeuristic::DuplicateFrameStateAndRename(
3901cb0ef41Sopenharmony_ci    FrameState frame_state, Node* from, Node* to, StateCloneMode mode) {
3911cb0ef41Sopenharmony_ci  // Only rename in states that are not shared with other users. This needs to
3921cb0ef41Sopenharmony_ci  // be in sync with the condition in {DuplicateFrameStateAndRename}.
3931cb0ef41Sopenharmony_ci  if (frame_state->UseCount() > 1) return frame_state;
3941cb0ef41Sopenharmony_ci  Node* copy =
3951cb0ef41Sopenharmony_ci      mode == kChangeInPlace ? static_cast<Node*>(frame_state) : nullptr;
3961cb0ef41Sopenharmony_ci  if (frame_state.stack() == from) {
3971cb0ef41Sopenharmony_ci    if (!copy) {
3981cb0ef41Sopenharmony_ci      copy = graph()->CloneNode(frame_state);
3991cb0ef41Sopenharmony_ci    }
4001cb0ef41Sopenharmony_ci    copy->ReplaceInput(FrameState::kFrameStateStackInput, to);
4011cb0ef41Sopenharmony_ci  }
4021cb0ef41Sopenharmony_ci  Node* locals = frame_state.locals();
4031cb0ef41Sopenharmony_ci  Node* new_locals = DuplicateStateValuesAndRename(locals, from, to, mode);
4041cb0ef41Sopenharmony_ci  if (new_locals != locals) {
4051cb0ef41Sopenharmony_ci    if (!copy) {
4061cb0ef41Sopenharmony_ci      copy = graph()->CloneNode(frame_state);
4071cb0ef41Sopenharmony_ci    }
4081cb0ef41Sopenharmony_ci    copy->ReplaceInput(FrameState::kFrameStateLocalsInput, new_locals);
4091cb0ef41Sopenharmony_ci  }
4101cb0ef41Sopenharmony_ci  return copy != nullptr ? FrameState{copy} : frame_state;
4111cb0ef41Sopenharmony_ci}
4121cb0ef41Sopenharmony_ci
4131cb0ef41Sopenharmony_cibool JSInliningHeuristic::TryReuseDispatch(Node* node, Node* callee,
4141cb0ef41Sopenharmony_ci                                           Node** if_successes, Node** calls,
4151cb0ef41Sopenharmony_ci                                           Node** inputs, int input_count) {
4161cb0ef41Sopenharmony_ci  // We will try to reuse the control flow branch created for computing
4171cb0ef41Sopenharmony_ci  // the {callee} target of the call. We only reuse the branch if there
4181cb0ef41Sopenharmony_ci  // is no side-effect between the call and the branch, and if the callee is
4191cb0ef41Sopenharmony_ci  // only used as the target (and possibly also in the related frame states).
4201cb0ef41Sopenharmony_ci
4211cb0ef41Sopenharmony_ci  // We are trying to match the following pattern:
4221cb0ef41Sopenharmony_ci  //
4231cb0ef41Sopenharmony_ci  //         C1     C2
4241cb0ef41Sopenharmony_ci  //          .     .
4251cb0ef41Sopenharmony_ci  //          |     |
4261cb0ef41Sopenharmony_ci  //         Merge(merge)  <-----------------+
4271cb0ef41Sopenharmony_ci  //           ^    ^                        |
4281cb0ef41Sopenharmony_ci  //  V1  V2   |    |         E1  E2         |
4291cb0ef41Sopenharmony_ci  //   .  .    |    +----+     .  .          |
4301cb0ef41Sopenharmony_ci  //   |  |    |         |     |  |          |
4311cb0ef41Sopenharmony_ci  //  Phi(callee)      EffectPhi(effect_phi) |
4321cb0ef41Sopenharmony_ci  //     ^                    ^              |
4331cb0ef41Sopenharmony_ci  //     |                    |              |
4341cb0ef41Sopenharmony_ci  //     +----+               |              |
4351cb0ef41Sopenharmony_ci  //     |    |               |              |
4361cb0ef41Sopenharmony_ci  //     |   StateValues      |              |
4371cb0ef41Sopenharmony_ci  //     |       ^            |              |
4381cb0ef41Sopenharmony_ci  //     +----+  |            |              |
4391cb0ef41Sopenharmony_ci  //     |    |  |            |              |
4401cb0ef41Sopenharmony_ci  //     |    FrameState      |              |
4411cb0ef41Sopenharmony_ci  //     |           ^        |              |
4421cb0ef41Sopenharmony_ci  //     |           |        |          +---+
4431cb0ef41Sopenharmony_ci  //     |           |        |          |   |
4441cb0ef41Sopenharmony_ci  //     +----+     Checkpoint(checkpoint)   |
4451cb0ef41Sopenharmony_ci  //     |    |           ^                  |
4461cb0ef41Sopenharmony_ci  //     |    StateValues |    +-------------+
4471cb0ef41Sopenharmony_ci  //     |        |       |    |
4481cb0ef41Sopenharmony_ci  //     +-----+  |       |    |
4491cb0ef41Sopenharmony_ci  //     |     |  |       |    |
4501cb0ef41Sopenharmony_ci  //     |     FrameState |    |
4511cb0ef41Sopenharmony_ci  //     |             ^  |    |
4521cb0ef41Sopenharmony_ci  //     +-----------+ |  |    |
4531cb0ef41Sopenharmony_ci  //                  Call(node)
4541cb0ef41Sopenharmony_ci  //                   |
4551cb0ef41Sopenharmony_ci  //                   |
4561cb0ef41Sopenharmony_ci  //                   .
4571cb0ef41Sopenharmony_ci  //
4581cb0ef41Sopenharmony_ci  // The {callee} here is a phi that merges the possible call targets, {node}
4591cb0ef41Sopenharmony_ci  // is the actual call that we will try to duplicate and connect to the
4601cb0ef41Sopenharmony_ci  // control that comes into {merge}. There can be a {checkpoint} between
4611cb0ef41Sopenharmony_ci  // the call and the calle phi.
4621cb0ef41Sopenharmony_ci  //
4631cb0ef41Sopenharmony_ci  // The idea is to get rid of the merge, effect phi and phi, then duplicate
4641cb0ef41Sopenharmony_ci  // the call (with all the frame states and such), and connect the duplicated
4651cb0ef41Sopenharmony_ci  // calls and states directly to the inputs of the ex-phi, ex-effect-phi and
4661cb0ef41Sopenharmony_ci  // ex-merge. The tricky part is to make sure that there is no interference
4671cb0ef41Sopenharmony_ci  // from the outside. In particular, there should not be any unaccounted uses
4681cb0ef41Sopenharmony_ci  // of the  phi, effect-phi and merge because we will remove them from
4691cb0ef41Sopenharmony_ci  // the graph.
4701cb0ef41Sopenharmony_ci  //
4711cb0ef41Sopenharmony_ci  //     V1              E1   C1  V2   E2               C2
4721cb0ef41Sopenharmony_ci  //     .                .    .  .    .                .
4731cb0ef41Sopenharmony_ci  //     |                |    |  |    |                |
4741cb0ef41Sopenharmony_ci  //     +----+           |    |  +----+                |
4751cb0ef41Sopenharmony_ci  //     |    |           |    |  |    |                |
4761cb0ef41Sopenharmony_ci  //     |   StateValues  |    |  |   StateValues       |
4771cb0ef41Sopenharmony_ci  //     |       ^        |    |  |       ^             |
4781cb0ef41Sopenharmony_ci  //     +----+  |        |    |  +----+  |             |
4791cb0ef41Sopenharmony_ci  //     |    |  |        |    |  |    |  |             |
4801cb0ef41Sopenharmony_ci  //     |    FrameState  |    |  |    FrameState       |
4811cb0ef41Sopenharmony_ci  //     |           ^    |    |  |           ^         |
4821cb0ef41Sopenharmony_ci  //     |           |    |    |  |           |         |
4831cb0ef41Sopenharmony_ci  //     |           |    |    |  |           |         |
4841cb0ef41Sopenharmony_ci  //     +----+     Checkpoint |  +----+     Checkpoint |
4851cb0ef41Sopenharmony_ci  //     |    |           ^    |  |    |           ^    |
4861cb0ef41Sopenharmony_ci  //     |    StateValues |    |  |    StateValues |    |
4871cb0ef41Sopenharmony_ci  //     |        |       |    |  |        |       |    |
4881cb0ef41Sopenharmony_ci  //     +-----+  |       |    |  +-----+  |       |    |
4891cb0ef41Sopenharmony_ci  //     |     |  |       |    |  |     |  |       |    |
4901cb0ef41Sopenharmony_ci  //     |     FrameState |    |  |     FrameState |    |
4911cb0ef41Sopenharmony_ci  //     |              ^ |    |  |              ^ |    |
4921cb0ef41Sopenharmony_ci  //     +-------------+| |    |  +-------------+| |    |
4931cb0ef41Sopenharmony_ci  //                   Call----+                Call----+
4941cb0ef41Sopenharmony_ci  //                     |                       |
4951cb0ef41Sopenharmony_ci  //                     +-------+  +------------+
4961cb0ef41Sopenharmony_ci  //                             |  |
4971cb0ef41Sopenharmony_ci  //                             Merge
4981cb0ef41Sopenharmony_ci  //                             EffectPhi
4991cb0ef41Sopenharmony_ci  //                             Phi
5001cb0ef41Sopenharmony_ci  //                              |
5011cb0ef41Sopenharmony_ci  //                             ...
5021cb0ef41Sopenharmony_ci
5031cb0ef41Sopenharmony_ci  // Bailout if the call is not polymorphic anymore (other reducers might
5041cb0ef41Sopenharmony_ci  // have replaced the callee phi with a constant).
5051cb0ef41Sopenharmony_ci  if (callee->opcode() != IrOpcode::kPhi) return false;
5061cb0ef41Sopenharmony_ci  int const num_calls = callee->op()->ValueInputCount();
5071cb0ef41Sopenharmony_ci
5081cb0ef41Sopenharmony_ci  // If there is a control node between the callee computation
5091cb0ef41Sopenharmony_ci  // and the call, bail out.
5101cb0ef41Sopenharmony_ci  Node* merge = NodeProperties::GetControlInput(callee);
5111cb0ef41Sopenharmony_ci  if (NodeProperties::GetControlInput(node) != merge) return false;
5121cb0ef41Sopenharmony_ci
5131cb0ef41Sopenharmony_ci  // If there is a non-checkpoint effect node between the callee computation
5141cb0ef41Sopenharmony_ci  // and the call, bail out. We will drop any checkpoint between the call and
5151cb0ef41Sopenharmony_ci  // the callee phi because the callee computation should have its own
5161cb0ef41Sopenharmony_ci  // checkpoint that the call can fall back to.
5171cb0ef41Sopenharmony_ci  Node* checkpoint = nullptr;
5181cb0ef41Sopenharmony_ci  Node* effect = NodeProperties::GetEffectInput(node);
5191cb0ef41Sopenharmony_ci  if (effect->opcode() == IrOpcode::kCheckpoint) {
5201cb0ef41Sopenharmony_ci    checkpoint = effect;
5211cb0ef41Sopenharmony_ci    if (NodeProperties::GetControlInput(checkpoint) != merge) return false;
5221cb0ef41Sopenharmony_ci    effect = NodeProperties::GetEffectInput(effect);
5231cb0ef41Sopenharmony_ci  }
5241cb0ef41Sopenharmony_ci  if (effect->opcode() != IrOpcode::kEffectPhi) return false;
5251cb0ef41Sopenharmony_ci  if (NodeProperties::GetControlInput(effect) != merge) return false;
5261cb0ef41Sopenharmony_ci  Node* effect_phi = effect;
5271cb0ef41Sopenharmony_ci
5281cb0ef41Sopenharmony_ci  // The effect phi, the callee, the call and the checkpoint must be the only
5291cb0ef41Sopenharmony_ci  // users of the merge.
5301cb0ef41Sopenharmony_ci  for (Node* merge_use : merge->uses()) {
5311cb0ef41Sopenharmony_ci    if (merge_use != effect_phi && merge_use != callee && merge_use != node &&
5321cb0ef41Sopenharmony_ci        merge_use != checkpoint) {
5331cb0ef41Sopenharmony_ci      return false;
5341cb0ef41Sopenharmony_ci    }
5351cb0ef41Sopenharmony_ci  }
5361cb0ef41Sopenharmony_ci
5371cb0ef41Sopenharmony_ci  // The effect phi must be only used by the checkpoint or the call.
5381cb0ef41Sopenharmony_ci  for (Node* effect_phi_use : effect_phi->uses()) {
5391cb0ef41Sopenharmony_ci    if (effect_phi_use != node && effect_phi_use != checkpoint) return false;
5401cb0ef41Sopenharmony_ci  }
5411cb0ef41Sopenharmony_ci
5421cb0ef41Sopenharmony_ci  // We must replace the callee phi with the appropriate constant in
5431cb0ef41Sopenharmony_ci  // the entire subgraph reachable by inputs from the call (terminating
5441cb0ef41Sopenharmony_ci  // at phis and merges). Since we do not want to walk (and later duplicate)
5451cb0ef41Sopenharmony_ci  // the subgraph here, we limit the possible uses to this set:
5461cb0ef41Sopenharmony_ci  //
5471cb0ef41Sopenharmony_ci  // 1. In the call (as a target).
5481cb0ef41Sopenharmony_ci  // 2. The checkpoint between the call and the callee computation merge.
5491cb0ef41Sopenharmony_ci  // 3. The lazy deoptimization frame state.
5501cb0ef41Sopenharmony_ci  //
5511cb0ef41Sopenharmony_ci  // This corresponds to the most common pattern, where the function is
5521cb0ef41Sopenharmony_ci  // called with only local variables or constants as arguments.
5531cb0ef41Sopenharmony_ci  //
5541cb0ef41Sopenharmony_ci  // To check the uses, we first collect all the occurrences of callee in 1, 2
5551cb0ef41Sopenharmony_ci  // and 3, and then we check that all uses of callee are in the collected
5561cb0ef41Sopenharmony_ci  // occurrences. If there is an unaccounted use, we do not try to rewire
5571cb0ef41Sopenharmony_ci  // the control flow.
5581cb0ef41Sopenharmony_ci  //
5591cb0ef41Sopenharmony_ci  // Note: With CFG, this would be much easier and more robust - we would just
5601cb0ef41Sopenharmony_ci  // duplicate all the nodes between the merge and the call, replacing all
5611cb0ef41Sopenharmony_ci  // occurrences of the {callee} phi with the appropriate constant.
5621cb0ef41Sopenharmony_ci
5631cb0ef41Sopenharmony_ci  // First compute the set of uses that are only reachable from 2 and 3.
5641cb0ef41Sopenharmony_ci  const size_t kMaxUses = 8;
5651cb0ef41Sopenharmony_ci  NodeAndIndex replaceable_uses[kMaxUses];
5661cb0ef41Sopenharmony_ci  size_t replaceable_uses_count = 0;
5671cb0ef41Sopenharmony_ci
5681cb0ef41Sopenharmony_ci  // Collect the uses to check case 2.
5691cb0ef41Sopenharmony_ci  Node* checkpoint_state = nullptr;
5701cb0ef41Sopenharmony_ci  if (checkpoint) {
5711cb0ef41Sopenharmony_ci    checkpoint_state = checkpoint->InputAt(0);
5721cb0ef41Sopenharmony_ci    if (!CollectFrameStateUniqueUses(callee, FrameState{checkpoint_state},
5731cb0ef41Sopenharmony_ci                                     replaceable_uses, &replaceable_uses_count,
5741cb0ef41Sopenharmony_ci                                     kMaxUses)) {
5751cb0ef41Sopenharmony_ci      return false;
5761cb0ef41Sopenharmony_ci    }
5771cb0ef41Sopenharmony_ci  }
5781cb0ef41Sopenharmony_ci
5791cb0ef41Sopenharmony_ci  // Collect the uses to check case 3.
5801cb0ef41Sopenharmony_ci  FrameState frame_state{NodeProperties::GetFrameStateInput(node)};
5811cb0ef41Sopenharmony_ci  if (!CollectFrameStateUniqueUses(callee, frame_state, replaceable_uses,
5821cb0ef41Sopenharmony_ci                                   &replaceable_uses_count, kMaxUses)) {
5831cb0ef41Sopenharmony_ci    return false;
5841cb0ef41Sopenharmony_ci  }
5851cb0ef41Sopenharmony_ci
5861cb0ef41Sopenharmony_ci  // Bail out if there is a use of {callee} that is not reachable from 1, 2
5871cb0ef41Sopenharmony_ci  // and 3.
5881cb0ef41Sopenharmony_ci  for (Edge edge : callee->use_edges()) {
5891cb0ef41Sopenharmony_ci    // Case 1 (use by the call as a target).
5901cb0ef41Sopenharmony_ci    if (edge.from() == node && edge.index() == 0) continue;
5911cb0ef41Sopenharmony_ci    // Case 2 and 3 - used in checkpoint and/or lazy deopt frame states.
5921cb0ef41Sopenharmony_ci    bool found = false;
5931cb0ef41Sopenharmony_ci    for (size_t i = 0; i < replaceable_uses_count; i++) {
5941cb0ef41Sopenharmony_ci      if (replaceable_uses[i].node == edge.from() &&
5951cb0ef41Sopenharmony_ci          replaceable_uses[i].index == edge.index()) {
5961cb0ef41Sopenharmony_ci        found = true;
5971cb0ef41Sopenharmony_ci        break;
5981cb0ef41Sopenharmony_ci      }
5991cb0ef41Sopenharmony_ci    }
6001cb0ef41Sopenharmony_ci    if (!found) return false;
6011cb0ef41Sopenharmony_ci  }
6021cb0ef41Sopenharmony_ci
6031cb0ef41Sopenharmony_ci  // Clone the call and the framestate, including the uniquely reachable
6041cb0ef41Sopenharmony_ci  // state values, making sure that we replace the phi with the constant.
6051cb0ef41Sopenharmony_ci  for (int i = 0; i < num_calls; ++i) {
6061cb0ef41Sopenharmony_ci    // Clone the calls for each branch.
6071cb0ef41Sopenharmony_ci    // We need to specialize the calls to the correct target, effect, and
6081cb0ef41Sopenharmony_ci    // control. We also need to duplicate the checkpoint and the lazy
6091cb0ef41Sopenharmony_ci    // frame state, and change all the uses of the callee to the constant
6101cb0ef41Sopenharmony_ci    // callee.
6111cb0ef41Sopenharmony_ci    Node* target = callee->InputAt(i);
6121cb0ef41Sopenharmony_ci    Node* effect_phi_effect = effect_phi->InputAt(i);
6131cb0ef41Sopenharmony_ci    Node* control = merge->InputAt(i);
6141cb0ef41Sopenharmony_ci
6151cb0ef41Sopenharmony_ci    if (checkpoint) {
6161cb0ef41Sopenharmony_ci      // Duplicate the checkpoint.
6171cb0ef41Sopenharmony_ci      FrameState new_checkpoint_state = DuplicateFrameStateAndRename(
6181cb0ef41Sopenharmony_ci          FrameState{checkpoint_state}, callee, target,
6191cb0ef41Sopenharmony_ci          (i == num_calls - 1) ? kChangeInPlace : kCloneState);
6201cb0ef41Sopenharmony_ci      effect_phi_effect = graph()->NewNode(
6211cb0ef41Sopenharmony_ci          checkpoint->op(), new_checkpoint_state, effect_phi_effect, control);
6221cb0ef41Sopenharmony_ci    }
6231cb0ef41Sopenharmony_ci
6241cb0ef41Sopenharmony_ci    // Duplicate the call.
6251cb0ef41Sopenharmony_ci    FrameState new_lazy_frame_state = DuplicateFrameStateAndRename(
6261cb0ef41Sopenharmony_ci        frame_state, callee, target,
6271cb0ef41Sopenharmony_ci        (i == num_calls - 1) ? kChangeInPlace : kCloneState);
6281cb0ef41Sopenharmony_ci    inputs[0] = target;
6291cb0ef41Sopenharmony_ci    inputs[input_count - 3] = new_lazy_frame_state;
6301cb0ef41Sopenharmony_ci    inputs[input_count - 2] = effect_phi_effect;
6311cb0ef41Sopenharmony_ci    inputs[input_count - 1] = control;
6321cb0ef41Sopenharmony_ci    calls[i] = if_successes[i] =
6331cb0ef41Sopenharmony_ci        graph()->NewNode(node->op(), input_count, inputs);
6341cb0ef41Sopenharmony_ci  }
6351cb0ef41Sopenharmony_ci
6361cb0ef41Sopenharmony_ci  // Mark the control inputs dead, so that we can kill the merge.
6371cb0ef41Sopenharmony_ci  node->ReplaceInput(input_count - 1, jsgraph()->Dead());
6381cb0ef41Sopenharmony_ci  callee->ReplaceInput(num_calls, jsgraph()->Dead());
6391cb0ef41Sopenharmony_ci  effect_phi->ReplaceInput(num_calls, jsgraph()->Dead());
6401cb0ef41Sopenharmony_ci  if (checkpoint) {
6411cb0ef41Sopenharmony_ci    checkpoint->ReplaceInput(2, jsgraph()->Dead());
6421cb0ef41Sopenharmony_ci  }
6431cb0ef41Sopenharmony_ci
6441cb0ef41Sopenharmony_ci  merge->Kill();
6451cb0ef41Sopenharmony_ci  return true;
6461cb0ef41Sopenharmony_ci}
6471cb0ef41Sopenharmony_ci
6481cb0ef41Sopenharmony_civoid JSInliningHeuristic::CreateOrReuseDispatch(Node* node, Node* callee,
6491cb0ef41Sopenharmony_ci                                                Candidate const& candidate,
6501cb0ef41Sopenharmony_ci                                                Node** if_successes,
6511cb0ef41Sopenharmony_ci                                                Node** calls, Node** inputs,
6521cb0ef41Sopenharmony_ci                                                int input_count) {
6531cb0ef41Sopenharmony_ci  SourcePositionTable::Scope position(
6541cb0ef41Sopenharmony_ci      source_positions_, source_positions_->GetSourcePosition(node));
6551cb0ef41Sopenharmony_ci  if (TryReuseDispatch(node, callee, if_successes, calls, inputs,
6561cb0ef41Sopenharmony_ci                       input_count)) {
6571cb0ef41Sopenharmony_ci    return;
6581cb0ef41Sopenharmony_ci  }
6591cb0ef41Sopenharmony_ci
6601cb0ef41Sopenharmony_ci  STATIC_ASSERT(JSCallOrConstructNode::kHaveIdenticalLayouts);
6611cb0ef41Sopenharmony_ci
6621cb0ef41Sopenharmony_ci  Node* fallthrough_control = NodeProperties::GetControlInput(node);
6631cb0ef41Sopenharmony_ci  int const num_calls = candidate.num_functions;
6641cb0ef41Sopenharmony_ci
6651cb0ef41Sopenharmony_ci  // Create the appropriate control flow to dispatch to the cloned calls.
6661cb0ef41Sopenharmony_ci  for (int i = 0; i < num_calls; ++i) {
6671cb0ef41Sopenharmony_ci    // TODO(2206): Make comparison be based on underlying SharedFunctionInfo
6681cb0ef41Sopenharmony_ci    // instead of the target JSFunction reference directly.
6691cb0ef41Sopenharmony_ci    Node* target = jsgraph()->Constant(candidate.functions[i].value());
6701cb0ef41Sopenharmony_ci    if (i != (num_calls - 1)) {
6711cb0ef41Sopenharmony_ci      Node* check =
6721cb0ef41Sopenharmony_ci          graph()->NewNode(simplified()->ReferenceEqual(), callee, target);
6731cb0ef41Sopenharmony_ci      Node* branch =
6741cb0ef41Sopenharmony_ci          graph()->NewNode(common()->Branch(), check, fallthrough_control);
6751cb0ef41Sopenharmony_ci      fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
6761cb0ef41Sopenharmony_ci      if_successes[i] = graph()->NewNode(common()->IfTrue(), branch);
6771cb0ef41Sopenharmony_ci    } else {
6781cb0ef41Sopenharmony_ci      if_successes[i] = fallthrough_control;
6791cb0ef41Sopenharmony_ci    }
6801cb0ef41Sopenharmony_ci
6811cb0ef41Sopenharmony_ci    // Clone the calls for each branch.
6821cb0ef41Sopenharmony_ci    // The first input to the call is the actual target (which we specialize
6831cb0ef41Sopenharmony_ci    // to the known {target}); the last input is the control dependency.
6841cb0ef41Sopenharmony_ci    // We also specialize the new.target of JSConstruct {node}s if it refers
6851cb0ef41Sopenharmony_ci    // to the same node as the {node}'s target input, so that we can later
6861cb0ef41Sopenharmony_ci    // properly inline the JSCreate operations.
6871cb0ef41Sopenharmony_ci    if (node->opcode() == IrOpcode::kJSConstruct) {
6881cb0ef41Sopenharmony_ci      // TODO(jgruber, v8:10675): This branch seems unreachable.
6891cb0ef41Sopenharmony_ci      JSConstructNode n(node);
6901cb0ef41Sopenharmony_ci      if (inputs[n.TargetIndex()] == inputs[n.NewTargetIndex()]) {
6911cb0ef41Sopenharmony_ci        inputs[n.NewTargetIndex()] = target;
6921cb0ef41Sopenharmony_ci      }
6931cb0ef41Sopenharmony_ci    }
6941cb0ef41Sopenharmony_ci    inputs[JSCallOrConstructNode::TargetIndex()] = target;
6951cb0ef41Sopenharmony_ci    inputs[input_count - 1] = if_successes[i];
6961cb0ef41Sopenharmony_ci    calls[i] = if_successes[i] =
6971cb0ef41Sopenharmony_ci        graph()->NewNode(node->op(), input_count, inputs);
6981cb0ef41Sopenharmony_ci  }
6991cb0ef41Sopenharmony_ci}
7001cb0ef41Sopenharmony_ci
7011cb0ef41Sopenharmony_ciReduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate,
7021cb0ef41Sopenharmony_ci                                               bool small_function) {
7031cb0ef41Sopenharmony_ci  int const num_calls = candidate.num_functions;
7041cb0ef41Sopenharmony_ci  Node* const node = candidate.node;
7051cb0ef41Sopenharmony_ci#if V8_ENABLE_WEBASSEMBLY
7061cb0ef41Sopenharmony_ci  DCHECK_NE(node->opcode(), IrOpcode::kJSWasmCall);
7071cb0ef41Sopenharmony_ci#endif  // V8_ENABLE_WEBASSEMBLY
7081cb0ef41Sopenharmony_ci  if (num_calls == 1) {
7091cb0ef41Sopenharmony_ci    Reduction const reduction = inliner_.ReduceJSCall(node);
7101cb0ef41Sopenharmony_ci    if (reduction.Changed()) {
7111cb0ef41Sopenharmony_ci      total_inlined_bytecode_size_ += candidate.bytecode[0].value().length();
7121cb0ef41Sopenharmony_ci    }
7131cb0ef41Sopenharmony_ci    return reduction;
7141cb0ef41Sopenharmony_ci  }
7151cb0ef41Sopenharmony_ci
7161cb0ef41Sopenharmony_ci  // Expand the JSCall/JSConstruct node to a subgraph first if
7171cb0ef41Sopenharmony_ci  // we have multiple known target functions.
7181cb0ef41Sopenharmony_ci  DCHECK_LT(1, num_calls);
7191cb0ef41Sopenharmony_ci  Node* calls[kMaxCallPolymorphism + 1];
7201cb0ef41Sopenharmony_ci  Node* if_successes[kMaxCallPolymorphism];
7211cb0ef41Sopenharmony_ci  Node* callee = NodeProperties::GetValueInput(node, 0);
7221cb0ef41Sopenharmony_ci
7231cb0ef41Sopenharmony_ci  // Setup the inputs for the cloned call nodes.
7241cb0ef41Sopenharmony_ci  int const input_count = node->InputCount();
7251cb0ef41Sopenharmony_ci  Node** inputs = graph()->zone()->NewArray<Node*>(input_count);
7261cb0ef41Sopenharmony_ci  for (int i = 0; i < input_count; ++i) {
7271cb0ef41Sopenharmony_ci    inputs[i] = node->InputAt(i);
7281cb0ef41Sopenharmony_ci  }
7291cb0ef41Sopenharmony_ci
7301cb0ef41Sopenharmony_ci  // Create the appropriate control flow to dispatch to the cloned calls.
7311cb0ef41Sopenharmony_ci  CreateOrReuseDispatch(node, callee, candidate, if_successes, calls, inputs,
7321cb0ef41Sopenharmony_ci                        input_count);
7331cb0ef41Sopenharmony_ci
7341cb0ef41Sopenharmony_ci  // Check if we have an exception projection for the call {node}.
7351cb0ef41Sopenharmony_ci  Node* if_exception = nullptr;
7361cb0ef41Sopenharmony_ci  if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
7371cb0ef41Sopenharmony_ci    Node* if_exceptions[kMaxCallPolymorphism + 1];
7381cb0ef41Sopenharmony_ci    for (int i = 0; i < num_calls; ++i) {
7391cb0ef41Sopenharmony_ci      if_successes[i] = graph()->NewNode(common()->IfSuccess(), calls[i]);
7401cb0ef41Sopenharmony_ci      if_exceptions[i] =
7411cb0ef41Sopenharmony_ci          graph()->NewNode(common()->IfException(), calls[i], calls[i]);
7421cb0ef41Sopenharmony_ci    }
7431cb0ef41Sopenharmony_ci
7441cb0ef41Sopenharmony_ci    // Morph the {if_exception} projection into a join.
7451cb0ef41Sopenharmony_ci    Node* exception_control =
7461cb0ef41Sopenharmony_ci        graph()->NewNode(common()->Merge(num_calls), num_calls, if_exceptions);
7471cb0ef41Sopenharmony_ci    if_exceptions[num_calls] = exception_control;
7481cb0ef41Sopenharmony_ci    Node* exception_effect = graph()->NewNode(common()->EffectPhi(num_calls),
7491cb0ef41Sopenharmony_ci                                              num_calls + 1, if_exceptions);
7501cb0ef41Sopenharmony_ci    Node* exception_value = graph()->NewNode(
7511cb0ef41Sopenharmony_ci        common()->Phi(MachineRepresentation::kTagged, num_calls), num_calls + 1,
7521cb0ef41Sopenharmony_ci        if_exceptions);
7531cb0ef41Sopenharmony_ci    ReplaceWithValue(if_exception, exception_value, exception_effect,
7541cb0ef41Sopenharmony_ci                     exception_control);
7551cb0ef41Sopenharmony_ci  }
7561cb0ef41Sopenharmony_ci
7571cb0ef41Sopenharmony_ci  // Morph the original call site into a join of the dispatched call sites.
7581cb0ef41Sopenharmony_ci  Node* control =
7591cb0ef41Sopenharmony_ci      graph()->NewNode(common()->Merge(num_calls), num_calls, if_successes);
7601cb0ef41Sopenharmony_ci  calls[num_calls] = control;
7611cb0ef41Sopenharmony_ci  Node* effect =
7621cb0ef41Sopenharmony_ci      graph()->NewNode(common()->EffectPhi(num_calls), num_calls + 1, calls);
7631cb0ef41Sopenharmony_ci  Node* value =
7641cb0ef41Sopenharmony_ci      graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, num_calls),
7651cb0ef41Sopenharmony_ci                       num_calls + 1, calls);
7661cb0ef41Sopenharmony_ci  ReplaceWithValue(node, value, effect, control);
7671cb0ef41Sopenharmony_ci
7681cb0ef41Sopenharmony_ci  // Inline the individual, cloned call sites.
7691cb0ef41Sopenharmony_ci  for (int i = 0; i < num_calls && total_inlined_bytecode_size_ <
7701cb0ef41Sopenharmony_ci                                       max_inlined_bytecode_size_absolute_;
7711cb0ef41Sopenharmony_ci       ++i) {
7721cb0ef41Sopenharmony_ci    if (candidate.can_inline_function[i] &&
7731cb0ef41Sopenharmony_ci        (small_function || total_inlined_bytecode_size_ <
7741cb0ef41Sopenharmony_ci                               max_inlined_bytecode_size_cumulative_)) {
7751cb0ef41Sopenharmony_ci      Node* call = calls[i];
7761cb0ef41Sopenharmony_ci      Reduction const reduction = inliner_.ReduceJSCall(call);
7771cb0ef41Sopenharmony_ci      if (reduction.Changed()) {
7781cb0ef41Sopenharmony_ci        total_inlined_bytecode_size_ += candidate.bytecode[i]->length();
7791cb0ef41Sopenharmony_ci        // Killing the call node is not strictly necessary, but it is safer to
7801cb0ef41Sopenharmony_ci        // make sure we do not resurrect the node.
7811cb0ef41Sopenharmony_ci        call->Kill();
7821cb0ef41Sopenharmony_ci      }
7831cb0ef41Sopenharmony_ci    }
7841cb0ef41Sopenharmony_ci  }
7851cb0ef41Sopenharmony_ci
7861cb0ef41Sopenharmony_ci  return Replace(value);
7871cb0ef41Sopenharmony_ci}
7881cb0ef41Sopenharmony_ci
7891cb0ef41Sopenharmony_cibool JSInliningHeuristic::CandidateCompare::operator()(
7901cb0ef41Sopenharmony_ci    const Candidate& left, const Candidate& right) const {
7911cb0ef41Sopenharmony_ci  if (right.frequency.IsUnknown()) {
7921cb0ef41Sopenharmony_ci    if (left.frequency.IsUnknown()) {
7931cb0ef41Sopenharmony_ci      // If left and right are both unknown then the ordering is indeterminate,
7941cb0ef41Sopenharmony_ci      // which breaks strict weak ordering requirements, so we fall back to the
7951cb0ef41Sopenharmony_ci      // node id as a tie breaker.
7961cb0ef41Sopenharmony_ci      return left.node->id() > right.node->id();
7971cb0ef41Sopenharmony_ci    }
7981cb0ef41Sopenharmony_ci    return true;
7991cb0ef41Sopenharmony_ci  } else if (left.frequency.IsUnknown()) {
8001cb0ef41Sopenharmony_ci    return false;
8011cb0ef41Sopenharmony_ci  } else if (left.frequency.value() > right.frequency.value()) {
8021cb0ef41Sopenharmony_ci    return true;
8031cb0ef41Sopenharmony_ci  } else if (left.frequency.value() < right.frequency.value()) {
8041cb0ef41Sopenharmony_ci    return false;
8051cb0ef41Sopenharmony_ci  } else {
8061cb0ef41Sopenharmony_ci    return left.node->id() > right.node->id();
8071cb0ef41Sopenharmony_ci  }
8081cb0ef41Sopenharmony_ci}
8091cb0ef41Sopenharmony_ci
8101cb0ef41Sopenharmony_civoid JSInliningHeuristic::PrintCandidates() {
8111cb0ef41Sopenharmony_ci  StdoutStream os;
8121cb0ef41Sopenharmony_ci  os << candidates_.size() << " candidate(s) for inlining:" << std::endl;
8131cb0ef41Sopenharmony_ci  for (const Candidate& candidate : candidates_) {
8141cb0ef41Sopenharmony_ci    os << "- candidate: " << candidate.node->op()->mnemonic() << " node #"
8151cb0ef41Sopenharmony_ci       << candidate.node->id() << " with frequency " << candidate.frequency
8161cb0ef41Sopenharmony_ci       << ", " << candidate.num_functions << " target(s):" << std::endl;
8171cb0ef41Sopenharmony_ci    for (int i = 0; i < candidate.num_functions; ++i) {
8181cb0ef41Sopenharmony_ci      SharedFunctionInfoRef shared = candidate.functions[i].has_value()
8191cb0ef41Sopenharmony_ci                                         ? candidate.functions[i]->shared()
8201cb0ef41Sopenharmony_ci                                         : candidate.shared_info.value();
8211cb0ef41Sopenharmony_ci      os << "  - target: " << shared;
8221cb0ef41Sopenharmony_ci      if (candidate.bytecode[i].has_value()) {
8231cb0ef41Sopenharmony_ci        os << ", bytecode size: " << candidate.bytecode[i]->length();
8241cb0ef41Sopenharmony_ci        if (candidate.functions[i].has_value()) {
8251cb0ef41Sopenharmony_ci          JSFunctionRef function = candidate.functions[i].value();
8261cb0ef41Sopenharmony_ci          unsigned inlined_bytecode_size =
8271cb0ef41Sopenharmony_ci              function.code().GetInlinedBytecodeSize();
8281cb0ef41Sopenharmony_ci          if (inlined_bytecode_size > 0) {
8291cb0ef41Sopenharmony_ci            os << ", existing opt code's inlined bytecode size: "
8301cb0ef41Sopenharmony_ci               << inlined_bytecode_size;
8311cb0ef41Sopenharmony_ci          }
8321cb0ef41Sopenharmony_ci        }
8331cb0ef41Sopenharmony_ci      } else {
8341cb0ef41Sopenharmony_ci        os << ", no bytecode";
8351cb0ef41Sopenharmony_ci      }
8361cb0ef41Sopenharmony_ci      os << std::endl;
8371cb0ef41Sopenharmony_ci    }
8381cb0ef41Sopenharmony_ci  }
8391cb0ef41Sopenharmony_ci}
8401cb0ef41Sopenharmony_ci
8411cb0ef41Sopenharmony_ciGraph* JSInliningHeuristic::graph() const { return jsgraph()->graph(); }
8421cb0ef41Sopenharmony_ci
8431cb0ef41Sopenharmony_ciCompilationDependencies* JSInliningHeuristic::dependencies() const {
8441cb0ef41Sopenharmony_ci  return broker()->dependencies();
8451cb0ef41Sopenharmony_ci}
8461cb0ef41Sopenharmony_ci
8471cb0ef41Sopenharmony_ciCommonOperatorBuilder* JSInliningHeuristic::common() const {
8481cb0ef41Sopenharmony_ci  return jsgraph()->common();
8491cb0ef41Sopenharmony_ci}
8501cb0ef41Sopenharmony_ci
8511cb0ef41Sopenharmony_ciSimplifiedOperatorBuilder* JSInliningHeuristic::simplified() const {
8521cb0ef41Sopenharmony_ci  return jsgraph()->simplified();
8531cb0ef41Sopenharmony_ci}
8541cb0ef41Sopenharmony_ci
8551cb0ef41Sopenharmony_ci#undef TRACE
8561cb0ef41Sopenharmony_ci
8571cb0ef41Sopenharmony_ci}  // namespace compiler
8581cb0ef41Sopenharmony_ci}  // namespace internal
8591cb0ef41Sopenharmony_ci}  // namespace v8
860