11cb0ef41Sopenharmony_ci// Copyright 2021 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/wasm-inlining.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include "src/compiler/all-nodes.h"
81cb0ef41Sopenharmony_ci#include "src/compiler/compiler-source-position-table.h"
91cb0ef41Sopenharmony_ci#include "src/compiler/node-matchers.h"
101cb0ef41Sopenharmony_ci#include "src/compiler/wasm-compiler.h"
111cb0ef41Sopenharmony_ci#include "src/wasm/function-body-decoder.h"
121cb0ef41Sopenharmony_ci#include "src/wasm/graph-builder-interface.h"
131cb0ef41Sopenharmony_ci#include "src/wasm/wasm-features.h"
141cb0ef41Sopenharmony_ci#include "src/wasm/wasm-module.h"
151cb0ef41Sopenharmony_ci#include "src/wasm/wasm-subtyping.h"
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_cinamespace v8 {
181cb0ef41Sopenharmony_cinamespace internal {
191cb0ef41Sopenharmony_cinamespace compiler {
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ciReduction WasmInliner::Reduce(Node* node) {
221cb0ef41Sopenharmony_ci  switch (node->opcode()) {
231cb0ef41Sopenharmony_ci    case IrOpcode::kCall:
241cb0ef41Sopenharmony_ci    case IrOpcode::kTailCall:
251cb0ef41Sopenharmony_ci      return ReduceCall(node);
261cb0ef41Sopenharmony_ci    default:
271cb0ef41Sopenharmony_ci      return NoChange();
281cb0ef41Sopenharmony_ci  }
291cb0ef41Sopenharmony_ci}
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ci#define TRACE(...) \
321cb0ef41Sopenharmony_ci  if (FLAG_trace_wasm_inlining) PrintF(__VA_ARGS__)
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_civoid WasmInliner::Trace(Node* call, int inlinee, const char* decision) {
351cb0ef41Sopenharmony_ci  TRACE("[function %d: considering node %d, call to %d: %s]\n", function_index_,
361cb0ef41Sopenharmony_ci        call->id(), inlinee, decision);
371cb0ef41Sopenharmony_ci}
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ciuint32_t WasmInliner::FindOriginatingFunction(Node* call) {
401cb0ef41Sopenharmony_ci  DCHECK_EQ(inlined_functions_.size(), first_node_id_.size());
411cb0ef41Sopenharmony_ci  NodeId id = call->id();
421cb0ef41Sopenharmony_ci  if (inlined_functions_.size() == 0 || id < first_node_id_[0]) {
431cb0ef41Sopenharmony_ci    return function_index_;
441cb0ef41Sopenharmony_ci  }
451cb0ef41Sopenharmony_ci  for (size_t i = 1; i < first_node_id_.size(); i++) {
461cb0ef41Sopenharmony_ci    if (id < first_node_id_[i]) return inlined_functions_[i - 1];
471cb0ef41Sopenharmony_ci  }
481cb0ef41Sopenharmony_ci  DCHECK_GE(id, first_node_id_.back());
491cb0ef41Sopenharmony_ci  return inlined_functions_.back();
501cb0ef41Sopenharmony_ci}
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ciint WasmInliner::GetCallCount(Node* call) {
531cb0ef41Sopenharmony_ci  if (!FLAG_wasm_speculative_inlining) return 0;
541cb0ef41Sopenharmony_ci  base::MutexGuard guard(&module()->type_feedback.mutex);
551cb0ef41Sopenharmony_ci  wasm::WasmCodePosition position =
561cb0ef41Sopenharmony_ci      source_positions_->GetSourcePosition(call).ScriptOffset();
571cb0ef41Sopenharmony_ci  uint32_t func = FindOriginatingFunction(call);
581cb0ef41Sopenharmony_ci  auto maybe_feedback =
591cb0ef41Sopenharmony_ci      module()->type_feedback.feedback_for_function.find(func);
601cb0ef41Sopenharmony_ci  if (maybe_feedback == module()->type_feedback.feedback_for_function.end()) {
611cb0ef41Sopenharmony_ci    return 0;
621cb0ef41Sopenharmony_ci  }
631cb0ef41Sopenharmony_ci  wasm::FunctionTypeFeedback feedback = maybe_feedback->second;
641cb0ef41Sopenharmony_ci  // It's possible that we haven't processed the feedback yet. Currently,
651cb0ef41Sopenharmony_ci  // this can happen for targets of call_direct that haven't gotten hot yet,
661cb0ef41Sopenharmony_ci  // and for functions where Liftoff bailed out.
671cb0ef41Sopenharmony_ci  if (feedback.feedback_vector.size() == 0) return 0;
681cb0ef41Sopenharmony_ci  auto index_in_vector = feedback.positions.find(position);
691cb0ef41Sopenharmony_ci  if (index_in_vector == feedback.positions.end()) return 0;
701cb0ef41Sopenharmony_ci  return feedback.feedback_vector[index_in_vector->second]
711cb0ef41Sopenharmony_ci      .absolute_call_frequency;
721cb0ef41Sopenharmony_ci}
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci// TODO(12166): Save inlined frames for trap/--trace-wasm purposes. Consider
751cb0ef41Sopenharmony_ci//              tail calls.
761cb0ef41Sopenharmony_ciReduction WasmInliner::ReduceCall(Node* call) {
771cb0ef41Sopenharmony_ci  DCHECK(call->opcode() == IrOpcode::kCall ||
781cb0ef41Sopenharmony_ci         call->opcode() == IrOpcode::kTailCall);
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci  if (seen_.find(call) != seen_.end()) {
811cb0ef41Sopenharmony_ci    TRACE("function %d: have already seen node %d, skipping\n", function_index_,
821cb0ef41Sopenharmony_ci          call->id());
831cb0ef41Sopenharmony_ci    return NoChange();
841cb0ef41Sopenharmony_ci  }
851cb0ef41Sopenharmony_ci  seen_.insert(call);
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci  Node* callee = NodeProperties::GetValueInput(call, 0);
881cb0ef41Sopenharmony_ci  IrOpcode::Value reloc_opcode = mcgraph_->machine()->Is32()
891cb0ef41Sopenharmony_ci                                     ? IrOpcode::kRelocatableInt32Constant
901cb0ef41Sopenharmony_ci                                     : IrOpcode::kRelocatableInt64Constant;
911cb0ef41Sopenharmony_ci  if (callee->opcode() != reloc_opcode) {
921cb0ef41Sopenharmony_ci    TRACE("[function %d: considering node %d... not a relocatable constant]\n",
931cb0ef41Sopenharmony_ci          function_index_, call->id());
941cb0ef41Sopenharmony_ci    return NoChange();
951cb0ef41Sopenharmony_ci  }
961cb0ef41Sopenharmony_ci  auto info = OpParameter<RelocatablePtrConstantInfo>(callee->op());
971cb0ef41Sopenharmony_ci  uint32_t inlinee_index = static_cast<uint32_t>(info.value());
981cb0ef41Sopenharmony_ci  if (info.rmode() != RelocInfo::WASM_CALL) {
991cb0ef41Sopenharmony_ci    Trace(call, inlinee_index, "not a wasm call");
1001cb0ef41Sopenharmony_ci    return NoChange();
1011cb0ef41Sopenharmony_ci  }
1021cb0ef41Sopenharmony_ci  if (inlinee_index < module()->num_imported_functions) {
1031cb0ef41Sopenharmony_ci    Trace(call, inlinee_index, "imported function");
1041cb0ef41Sopenharmony_ci    return NoChange();
1051cb0ef41Sopenharmony_ci  }
1061cb0ef41Sopenharmony_ci  if (inlinee_index == function_index_) {
1071cb0ef41Sopenharmony_ci    Trace(call, inlinee_index, "recursive call");
1081cb0ef41Sopenharmony_ci    return NoChange();
1091cb0ef41Sopenharmony_ci  }
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci  Trace(call, inlinee_index, "adding to inlining candidates!");
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci  int call_count = GetCallCount(call);
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_ci  CHECK_LT(inlinee_index, module()->functions.size());
1161cb0ef41Sopenharmony_ci  const wasm::WasmFunction* inlinee = &module()->functions[inlinee_index];
1171cb0ef41Sopenharmony_ci  base::Vector<const byte> function_bytes = wire_bytes_->GetCode(inlinee->code);
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  CandidateInfo candidate{call, inlinee_index, call_count,
1201cb0ef41Sopenharmony_ci                          function_bytes.length()};
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci  inlining_candidates_.push(candidate);
1231cb0ef41Sopenharmony_ci  return NoChange();
1241cb0ef41Sopenharmony_ci}
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_cibool SmallEnoughToInline(size_t current_graph_size, uint32_t candidate_size) {
1271cb0ef41Sopenharmony_ci  if (WasmInliner::graph_size_allows_inlining(current_graph_size)) {
1281cb0ef41Sopenharmony_ci    return true;
1291cb0ef41Sopenharmony_ci  }
1301cb0ef41Sopenharmony_ci  // For truly tiny functions, let's be a bit more generous.
1311cb0ef41Sopenharmony_ci  return candidate_size < 10 &&
1321cb0ef41Sopenharmony_ci         WasmInliner::graph_size_allows_inlining(current_graph_size - 100);
1331cb0ef41Sopenharmony_ci}
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_civoid WasmInliner::Trace(const CandidateInfo& candidate, const char* decision) {
1361cb0ef41Sopenharmony_ci  TRACE(
1371cb0ef41Sopenharmony_ci      "  [function %d: considering candidate {@%d, index=%d, count=%d, "
1381cb0ef41Sopenharmony_ci      "size=%d}: %s]\n",
1391cb0ef41Sopenharmony_ci      function_index_, candidate.node->id(), candidate.inlinee_index,
1401cb0ef41Sopenharmony_ci      candidate.call_count, candidate.wire_byte_size, decision);
1411cb0ef41Sopenharmony_ci}
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_civoid WasmInliner::Finalize() {
1441cb0ef41Sopenharmony_ci  TRACE("function %d %s: going though inlining candidates...\n",
1451cb0ef41Sopenharmony_ci        function_index_, debug_name_);
1461cb0ef41Sopenharmony_ci  if (inlining_candidates_.empty()) return;
1471cb0ef41Sopenharmony_ci  while (!inlining_candidates_.empty()) {
1481cb0ef41Sopenharmony_ci    CandidateInfo candidate = inlining_candidates_.top();
1491cb0ef41Sopenharmony_ci    inlining_candidates_.pop();
1501cb0ef41Sopenharmony_ci    Node* call = candidate.node;
1511cb0ef41Sopenharmony_ci    if (call->IsDead()) {
1521cb0ef41Sopenharmony_ci      Trace(candidate, "dead node");
1531cb0ef41Sopenharmony_ci      continue;
1541cb0ef41Sopenharmony_ci    }
1551cb0ef41Sopenharmony_ci    int min_count_for_inlining = candidate.wire_byte_size / 2;
1561cb0ef41Sopenharmony_ci    if (candidate.call_count < min_count_for_inlining) {
1571cb0ef41Sopenharmony_ci      Trace(candidate, "not called often enough");
1581cb0ef41Sopenharmony_ci      continue;
1591cb0ef41Sopenharmony_ci    }
1601cb0ef41Sopenharmony_ci    // We could build the candidate's graph first and consider its node count,
1611cb0ef41Sopenharmony_ci    // but it turns out that wire byte size and node count are quite strongly
1621cb0ef41Sopenharmony_ci    // correlated, at about 1.16 nodes per wire byte (measured for J2Wasm).
1631cb0ef41Sopenharmony_ci    if (!SmallEnoughToInline(current_graph_size_, candidate.wire_byte_size)) {
1641cb0ef41Sopenharmony_ci      Trace(candidate, "not enough inlining budget");
1651cb0ef41Sopenharmony_ci      continue;
1661cb0ef41Sopenharmony_ci    }
1671cb0ef41Sopenharmony_ci    const wasm::WasmFunction* inlinee =
1681cb0ef41Sopenharmony_ci        &module()->functions[candidate.inlinee_index];
1691cb0ef41Sopenharmony_ci    base::Vector<const byte> function_bytes =
1701cb0ef41Sopenharmony_ci        wire_bytes_->GetCode(inlinee->code);
1711cb0ef41Sopenharmony_ci    // We use the signature based on the real argument types stored in the call
1721cb0ef41Sopenharmony_ci    // node. This is more specific than the callee's formal signature and might
1731cb0ef41Sopenharmony_ci    // enable some optimizations.
1741cb0ef41Sopenharmony_ci    const wasm::FunctionSig* specialized_sig =
1751cb0ef41Sopenharmony_ci        CallDescriptorOf(call->op())->wasm_sig();
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci#if DEBUG
1781cb0ef41Sopenharmony_ci    // Check that the real signature is a subtype of the formal one.
1791cb0ef41Sopenharmony_ci    const wasm::FunctionSig* formal_sig =
1801cb0ef41Sopenharmony_ci        WasmGraphBuilder::Int64LoweredSig(zone(), inlinee->sig);
1811cb0ef41Sopenharmony_ci    CHECK_EQ(specialized_sig->parameter_count(), formal_sig->parameter_count());
1821cb0ef41Sopenharmony_ci    CHECK_EQ(specialized_sig->return_count(), formal_sig->return_count());
1831cb0ef41Sopenharmony_ci    for (size_t i = 0; i < specialized_sig->parameter_count(); i++) {
1841cb0ef41Sopenharmony_ci      CHECK(wasm::IsSubtypeOf(specialized_sig->GetParam(i),
1851cb0ef41Sopenharmony_ci                              formal_sig->GetParam(i), module()));
1861cb0ef41Sopenharmony_ci    }
1871cb0ef41Sopenharmony_ci    for (size_t i = 0; i < specialized_sig->return_count(); i++) {
1881cb0ef41Sopenharmony_ci      CHECK(wasm::IsSubtypeOf(formal_sig->GetReturn(i),
1891cb0ef41Sopenharmony_ci                              specialized_sig->GetReturn(i), module()));
1901cb0ef41Sopenharmony_ci    }
1911cb0ef41Sopenharmony_ci#endif
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci    wasm::WasmFeatures detected;
1941cb0ef41Sopenharmony_ci    std::vector<WasmLoopInfo> inlinee_loop_infos;
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_ci    size_t subgraph_min_node_id = graph()->NodeCount();
1971cb0ef41Sopenharmony_ci    Node* inlinee_start;
1981cb0ef41Sopenharmony_ci    Node* inlinee_end;
1991cb0ef41Sopenharmony_ci    for (const wasm::FunctionSig* sig = specialized_sig;;) {
2001cb0ef41Sopenharmony_ci      const wasm::FunctionBody inlinee_body(sig, inlinee->code.offset(),
2011cb0ef41Sopenharmony_ci                                            function_bytes.begin(),
2021cb0ef41Sopenharmony_ci                                            function_bytes.end());
2031cb0ef41Sopenharmony_ci      WasmGraphBuilder builder(env_, zone(), mcgraph_, inlinee_body.sig,
2041cb0ef41Sopenharmony_ci                               source_positions_);
2051cb0ef41Sopenharmony_ci      Graph::SubgraphScope scope(graph());
2061cb0ef41Sopenharmony_ci      wasm::DecodeResult result = wasm::BuildTFGraph(
2071cb0ef41Sopenharmony_ci          zone()->allocator(), env_->enabled_features, module(), &builder,
2081cb0ef41Sopenharmony_ci          &detected, inlinee_body, &inlinee_loop_infos, node_origins_,
2091cb0ef41Sopenharmony_ci          candidate.inlinee_index,
2101cb0ef41Sopenharmony_ci          NodeProperties::IsExceptionalCall(call)
2111cb0ef41Sopenharmony_ci              ? wasm::kInlinedHandledCall
2121cb0ef41Sopenharmony_ci              : wasm::kInlinedNonHandledCall);
2131cb0ef41Sopenharmony_ci      if (result.ok()) {
2141cb0ef41Sopenharmony_ci        builder.LowerInt64(WasmGraphBuilder::kCalledFromWasm);
2151cb0ef41Sopenharmony_ci        inlinee_start = graph()->start();
2161cb0ef41Sopenharmony_ci        inlinee_end = graph()->end();
2171cb0ef41Sopenharmony_ci        break;
2181cb0ef41Sopenharmony_ci      }
2191cb0ef41Sopenharmony_ci      if (sig == specialized_sig) {
2201cb0ef41Sopenharmony_ci        // One possible reason for failure is the opportunistic signature
2211cb0ef41Sopenharmony_ci        // specialization. Try again without that.
2221cb0ef41Sopenharmony_ci        sig = inlinee->sig;
2231cb0ef41Sopenharmony_ci        inlinee_loop_infos.clear();
2241cb0ef41Sopenharmony_ci        Trace(candidate, "retrying with original signature");
2251cb0ef41Sopenharmony_ci        continue;
2261cb0ef41Sopenharmony_ci      }
2271cb0ef41Sopenharmony_ci      // Otherwise report failure.
2281cb0ef41Sopenharmony_ci      Trace(candidate, "failed to compile");
2291cb0ef41Sopenharmony_ci      return;
2301cb0ef41Sopenharmony_ci    }
2311cb0ef41Sopenharmony_ci
2321cb0ef41Sopenharmony_ci    size_t additional_nodes = graph()->NodeCount() - subgraph_min_node_id;
2331cb0ef41Sopenharmony_ci    Trace(candidate, "inlining!");
2341cb0ef41Sopenharmony_ci    current_graph_size_ += additional_nodes;
2351cb0ef41Sopenharmony_ci    inlined_functions_.push_back(candidate.inlinee_index);
2361cb0ef41Sopenharmony_ci    static_assert(std::is_same_v<NodeId, uint32_t>);
2371cb0ef41Sopenharmony_ci    first_node_id_.push_back(static_cast<uint32_t>(subgraph_min_node_id));
2381cb0ef41Sopenharmony_ci
2391cb0ef41Sopenharmony_ci    if (call->opcode() == IrOpcode::kCall) {
2401cb0ef41Sopenharmony_ci      InlineCall(call, inlinee_start, inlinee_end, inlinee->sig,
2411cb0ef41Sopenharmony_ci                 subgraph_min_node_id);
2421cb0ef41Sopenharmony_ci    } else {
2431cb0ef41Sopenharmony_ci      InlineTailCall(call, inlinee_start, inlinee_end);
2441cb0ef41Sopenharmony_ci    }
2451cb0ef41Sopenharmony_ci    call->Kill();
2461cb0ef41Sopenharmony_ci    loop_infos_->insert(loop_infos_->end(), inlinee_loop_infos.begin(),
2471cb0ef41Sopenharmony_ci                        inlinee_loop_infos.end());
2481cb0ef41Sopenharmony_ci    // Returning after only one inlining has been tried and found worse.
2491cb0ef41Sopenharmony_ci  }
2501cb0ef41Sopenharmony_ci}
2511cb0ef41Sopenharmony_ci
2521cb0ef41Sopenharmony_ci/* Rewire callee formal parameters to the call-site real parameters. Rewire
2531cb0ef41Sopenharmony_ci * effect and control dependencies of callee's start node with the respective
2541cb0ef41Sopenharmony_ci * inputs of the call node.
2551cb0ef41Sopenharmony_ci */
2561cb0ef41Sopenharmony_civoid WasmInliner::RewireFunctionEntry(Node* call, Node* callee_start) {
2571cb0ef41Sopenharmony_ci  Node* control = NodeProperties::GetControlInput(call);
2581cb0ef41Sopenharmony_ci  Node* effect = NodeProperties::GetEffectInput(call);
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ci  for (Edge edge : callee_start->use_edges()) {
2611cb0ef41Sopenharmony_ci    Node* use = edge.from();
2621cb0ef41Sopenharmony_ci    switch (use->opcode()) {
2631cb0ef41Sopenharmony_ci      case IrOpcode::kParameter: {
2641cb0ef41Sopenharmony_ci        // Index 0 is the callee node.
2651cb0ef41Sopenharmony_ci        int index = 1 + ParameterIndexOf(use->op());
2661cb0ef41Sopenharmony_ci        Replace(use, NodeProperties::GetValueInput(call, index));
2671cb0ef41Sopenharmony_ci        break;
2681cb0ef41Sopenharmony_ci      }
2691cb0ef41Sopenharmony_ci      default:
2701cb0ef41Sopenharmony_ci        if (NodeProperties::IsEffectEdge(edge)) {
2711cb0ef41Sopenharmony_ci          edge.UpdateTo(effect);
2721cb0ef41Sopenharmony_ci        } else if (NodeProperties::IsControlEdge(edge)) {
2731cb0ef41Sopenharmony_ci          // Projections pointing to the inlinee start are floating control.
2741cb0ef41Sopenharmony_ci          // They should point to the graph's start.
2751cb0ef41Sopenharmony_ci          edge.UpdateTo(use->opcode() == IrOpcode::kProjection
2761cb0ef41Sopenharmony_ci                            ? graph()->start()
2771cb0ef41Sopenharmony_ci                            : control);
2781cb0ef41Sopenharmony_ci        } else {
2791cb0ef41Sopenharmony_ci          UNREACHABLE();
2801cb0ef41Sopenharmony_ci        }
2811cb0ef41Sopenharmony_ci        Revisit(edge.from());
2821cb0ef41Sopenharmony_ci        break;
2831cb0ef41Sopenharmony_ci    }
2841cb0ef41Sopenharmony_ci  }
2851cb0ef41Sopenharmony_ci}
2861cb0ef41Sopenharmony_ci
2871cb0ef41Sopenharmony_civoid WasmInliner::InlineTailCall(Node* call, Node* callee_start,
2881cb0ef41Sopenharmony_ci                                 Node* callee_end) {
2891cb0ef41Sopenharmony_ci  DCHECK_EQ(call->opcode(), IrOpcode::kTailCall);
2901cb0ef41Sopenharmony_ci  // 1) Rewire function entry.
2911cb0ef41Sopenharmony_ci  RewireFunctionEntry(call, callee_start);
2921cb0ef41Sopenharmony_ci  // 2) For tail calls, all we have to do is rewire all terminators of the
2931cb0ef41Sopenharmony_ci  // inlined graph to the end of the caller graph.
2941cb0ef41Sopenharmony_ci  for (Node* const input : callee_end->inputs()) {
2951cb0ef41Sopenharmony_ci    DCHECK(IrOpcode::IsGraphTerminator(input->opcode()));
2961cb0ef41Sopenharmony_ci    NodeProperties::MergeControlToEnd(graph(), common(), input);
2971cb0ef41Sopenharmony_ci  }
2981cb0ef41Sopenharmony_ci  for (Edge edge_to_end : call->use_edges()) {
2991cb0ef41Sopenharmony_ci    DCHECK_EQ(edge_to_end.from(), graph()->end());
3001cb0ef41Sopenharmony_ci    edge_to_end.UpdateTo(mcgraph()->Dead());
3011cb0ef41Sopenharmony_ci  }
3021cb0ef41Sopenharmony_ci  callee_end->Kill();
3031cb0ef41Sopenharmony_ci  call->Kill();
3041cb0ef41Sopenharmony_ci  Revisit(graph()->end());
3051cb0ef41Sopenharmony_ci}
3061cb0ef41Sopenharmony_ci
3071cb0ef41Sopenharmony_cinamespace {
3081cb0ef41Sopenharmony_ci// graph-builder-interface generates a dangling exception handler for each
3091cb0ef41Sopenharmony_ci// throwing call in the inlinee. This might be followed by a LoopExit node.
3101cb0ef41Sopenharmony_ciNode* DanglingHandler(Node* call) {
3111cb0ef41Sopenharmony_ci  Node* if_exception = nullptr;
3121cb0ef41Sopenharmony_ci  for (Node* use : call->uses()) {
3131cb0ef41Sopenharmony_ci    if (use->opcode() == IrOpcode::kIfException) {
3141cb0ef41Sopenharmony_ci      if_exception = use;
3151cb0ef41Sopenharmony_ci      break;
3161cb0ef41Sopenharmony_ci    }
3171cb0ef41Sopenharmony_ci  }
3181cb0ef41Sopenharmony_ci  DCHECK_NOT_NULL(if_exception);
3191cb0ef41Sopenharmony_ci
3201cb0ef41Sopenharmony_ci  // If this handler is dangling, return it.
3211cb0ef41Sopenharmony_ci  if (if_exception->UseCount() == 0) return if_exception;
3221cb0ef41Sopenharmony_ci
3231cb0ef41Sopenharmony_ci  for (Node* use : if_exception->uses()) {
3241cb0ef41Sopenharmony_ci    // Otherwise, look for a LoopExit use of this handler.
3251cb0ef41Sopenharmony_ci    if (use->opcode() == IrOpcode::kLoopExit) {
3261cb0ef41Sopenharmony_ci      for (Node* loop_exit_use : use->uses()) {
3271cb0ef41Sopenharmony_ci        if (loop_exit_use->opcode() != IrOpcode::kLoopExitEffect &&
3281cb0ef41Sopenharmony_ci            loop_exit_use->opcode() != IrOpcode::kLoopExitValue) {
3291cb0ef41Sopenharmony_ci          // This LoopExit has a use other than LoopExitEffect/Value, so it is
3301cb0ef41Sopenharmony_ci          // not dangling.
3311cb0ef41Sopenharmony_ci          return nullptr;
3321cb0ef41Sopenharmony_ci        }
3331cb0ef41Sopenharmony_ci      }
3341cb0ef41Sopenharmony_ci      return use;
3351cb0ef41Sopenharmony_ci    }
3361cb0ef41Sopenharmony_ci  }
3371cb0ef41Sopenharmony_ci
3381cb0ef41Sopenharmony_ci  return nullptr;
3391cb0ef41Sopenharmony_ci}
3401cb0ef41Sopenharmony_ci}  // namespace
3411cb0ef41Sopenharmony_ci
3421cb0ef41Sopenharmony_civoid WasmInliner::InlineCall(Node* call, Node* callee_start, Node* callee_end,
3431cb0ef41Sopenharmony_ci                             const wasm::FunctionSig* inlinee_sig,
3441cb0ef41Sopenharmony_ci                             size_t subgraph_min_node_id) {
3451cb0ef41Sopenharmony_ci  DCHECK_EQ(call->opcode(), IrOpcode::kCall);
3461cb0ef41Sopenharmony_ci
3471cb0ef41Sopenharmony_ci  // 0) Before doing anything, if {call} has an exception handler, collect all
3481cb0ef41Sopenharmony_ci  // unhandled calls in the subgraph.
3491cb0ef41Sopenharmony_ci  Node* handler = nullptr;
3501cb0ef41Sopenharmony_ci  std::vector<Node*> dangling_handlers;
3511cb0ef41Sopenharmony_ci  if (NodeProperties::IsExceptionalCall(call, &handler)) {
3521cb0ef41Sopenharmony_ci    AllNodes subgraph_nodes(zone(), callee_end, graph());
3531cb0ef41Sopenharmony_ci    for (Node* node : subgraph_nodes.reachable) {
3541cb0ef41Sopenharmony_ci      if (node->id() >= subgraph_min_node_id &&
3551cb0ef41Sopenharmony_ci          !node->op()->HasProperty(Operator::kNoThrow)) {
3561cb0ef41Sopenharmony_ci        Node* dangling_handler = DanglingHandler(node);
3571cb0ef41Sopenharmony_ci        if (dangling_handler != nullptr) {
3581cb0ef41Sopenharmony_ci          dangling_handlers.push_back(dangling_handler);
3591cb0ef41Sopenharmony_ci        }
3601cb0ef41Sopenharmony_ci      }
3611cb0ef41Sopenharmony_ci    }
3621cb0ef41Sopenharmony_ci  }
3631cb0ef41Sopenharmony_ci
3641cb0ef41Sopenharmony_ci  // 1) Rewire function entry.
3651cb0ef41Sopenharmony_ci  RewireFunctionEntry(call, callee_start);
3661cb0ef41Sopenharmony_ci
3671cb0ef41Sopenharmony_ci  // 2) Handle all graph terminators for the callee.
3681cb0ef41Sopenharmony_ci  NodeVector return_nodes(zone());
3691cb0ef41Sopenharmony_ci  for (Node* const input : callee_end->inputs()) {
3701cb0ef41Sopenharmony_ci    DCHECK(IrOpcode::IsGraphTerminator(input->opcode()));
3711cb0ef41Sopenharmony_ci    switch (input->opcode()) {
3721cb0ef41Sopenharmony_ci      case IrOpcode::kReturn:
3731cb0ef41Sopenharmony_ci        // Returns are collected to be rewired into the caller graph later.
3741cb0ef41Sopenharmony_ci        return_nodes.push_back(input);
3751cb0ef41Sopenharmony_ci        break;
3761cb0ef41Sopenharmony_ci      case IrOpcode::kDeoptimize:
3771cb0ef41Sopenharmony_ci      case IrOpcode::kTerminate:
3781cb0ef41Sopenharmony_ci      case IrOpcode::kThrow:
3791cb0ef41Sopenharmony_ci        NodeProperties::MergeControlToEnd(graph(), common(), input);
3801cb0ef41Sopenharmony_ci        Revisit(graph()->end());
3811cb0ef41Sopenharmony_ci        break;
3821cb0ef41Sopenharmony_ci      case IrOpcode::kTailCall: {
3831cb0ef41Sopenharmony_ci        // A tail call in the callee inlined in a regular call in the caller has
3841cb0ef41Sopenharmony_ci        // to be transformed into a regular call, and then returned from the
3851cb0ef41Sopenharmony_ci        // inlinee. It will then be handled like any other return.
3861cb0ef41Sopenharmony_ci        auto descriptor = CallDescriptorOf(input->op());
3871cb0ef41Sopenharmony_ci        NodeProperties::ChangeOp(input, common()->Call(descriptor));
3881cb0ef41Sopenharmony_ci        int return_arity = static_cast<int>(inlinee_sig->return_count());
3891cb0ef41Sopenharmony_ci        NodeVector return_inputs(zone());
3901cb0ef41Sopenharmony_ci        // The first input of a return node is always the 0 constant.
3911cb0ef41Sopenharmony_ci        return_inputs.push_back(graph()->NewNode(common()->Int32Constant(0)));
3921cb0ef41Sopenharmony_ci        if (return_arity == 1) {
3931cb0ef41Sopenharmony_ci          return_inputs.push_back(input);
3941cb0ef41Sopenharmony_ci        } else if (return_arity > 1) {
3951cb0ef41Sopenharmony_ci          for (int i = 0; i < return_arity; i++) {
3961cb0ef41Sopenharmony_ci            return_inputs.push_back(
3971cb0ef41Sopenharmony_ci                graph()->NewNode(common()->Projection(i), input, input));
3981cb0ef41Sopenharmony_ci          }
3991cb0ef41Sopenharmony_ci        }
4001cb0ef41Sopenharmony_ci
4011cb0ef41Sopenharmony_ci        // Add effect and control inputs.
4021cb0ef41Sopenharmony_ci        return_inputs.push_back(input->op()->EffectOutputCount() > 0
4031cb0ef41Sopenharmony_ci                                    ? input
4041cb0ef41Sopenharmony_ci                                    : NodeProperties::GetEffectInput(input));
4051cb0ef41Sopenharmony_ci        return_inputs.push_back(input->op()->ControlOutputCount() > 0
4061cb0ef41Sopenharmony_ci                                    ? input
4071cb0ef41Sopenharmony_ci                                    : NodeProperties::GetControlInput(input));
4081cb0ef41Sopenharmony_ci
4091cb0ef41Sopenharmony_ci        Node* ret = graph()->NewNode(common()->Return(return_arity),
4101cb0ef41Sopenharmony_ci                                     static_cast<int>(return_inputs.size()),
4111cb0ef41Sopenharmony_ci                                     return_inputs.data());
4121cb0ef41Sopenharmony_ci        return_nodes.push_back(ret);
4131cb0ef41Sopenharmony_ci        break;
4141cb0ef41Sopenharmony_ci      }
4151cb0ef41Sopenharmony_ci      default:
4161cb0ef41Sopenharmony_ci        UNREACHABLE();
4171cb0ef41Sopenharmony_ci    }
4181cb0ef41Sopenharmony_ci  }
4191cb0ef41Sopenharmony_ci  callee_end->Kill();
4201cb0ef41Sopenharmony_ci
4211cb0ef41Sopenharmony_ci  // 3) Rewire unhandled calls to the handler.
4221cb0ef41Sopenharmony_ci  int handler_count = static_cast<int>(dangling_handlers.size());
4231cb0ef41Sopenharmony_ci
4241cb0ef41Sopenharmony_ci  if (handler_count > 0) {
4251cb0ef41Sopenharmony_ci    Node* control_output =
4261cb0ef41Sopenharmony_ci        graph()->NewNode(common()->Merge(handler_count), handler_count,
4271cb0ef41Sopenharmony_ci                         dangling_handlers.data());
4281cb0ef41Sopenharmony_ci    std::vector<Node*> effects;
4291cb0ef41Sopenharmony_ci    std::vector<Node*> values;
4301cb0ef41Sopenharmony_ci    for (Node* control : dangling_handlers) {
4311cb0ef41Sopenharmony_ci      if (control->opcode() == IrOpcode::kIfException) {
4321cb0ef41Sopenharmony_ci        effects.push_back(control);
4331cb0ef41Sopenharmony_ci        values.push_back(control);
4341cb0ef41Sopenharmony_ci      } else {
4351cb0ef41Sopenharmony_ci        DCHECK_EQ(control->opcode(), IrOpcode::kLoopExit);
4361cb0ef41Sopenharmony_ci        Node* if_exception = control->InputAt(0);
4371cb0ef41Sopenharmony_ci        DCHECK_EQ(if_exception->opcode(), IrOpcode::kIfException);
4381cb0ef41Sopenharmony_ci        effects.push_back(graph()->NewNode(common()->LoopExitEffect(),
4391cb0ef41Sopenharmony_ci                                           if_exception, control));
4401cb0ef41Sopenharmony_ci        values.push_back(graph()->NewNode(
4411cb0ef41Sopenharmony_ci            common()->LoopExitValue(MachineRepresentation::kTagged),
4421cb0ef41Sopenharmony_ci            if_exception, control));
4431cb0ef41Sopenharmony_ci      }
4441cb0ef41Sopenharmony_ci    }
4451cb0ef41Sopenharmony_ci
4461cb0ef41Sopenharmony_ci    effects.push_back(control_output);
4471cb0ef41Sopenharmony_ci    values.push_back(control_output);
4481cb0ef41Sopenharmony_ci    Node* value_output = graph()->NewNode(
4491cb0ef41Sopenharmony_ci        common()->Phi(MachineRepresentation::kTagged, handler_count),
4501cb0ef41Sopenharmony_ci        handler_count + 1, values.data());
4511cb0ef41Sopenharmony_ci    Node* effect_output = graph()->NewNode(common()->EffectPhi(handler_count),
4521cb0ef41Sopenharmony_ci                                           handler_count + 1, effects.data());
4531cb0ef41Sopenharmony_ci    ReplaceWithValue(handler, value_output, effect_output, control_output);
4541cb0ef41Sopenharmony_ci  } else if (handler != nullptr) {
4551cb0ef41Sopenharmony_ci    // Nothing in the inlined function can throw. Remove the handler.
4561cb0ef41Sopenharmony_ci    ReplaceWithValue(handler, mcgraph()->Dead(), mcgraph()->Dead(),
4571cb0ef41Sopenharmony_ci                     mcgraph()->Dead());
4581cb0ef41Sopenharmony_ci  }
4591cb0ef41Sopenharmony_ci
4601cb0ef41Sopenharmony_ci  if (return_nodes.size() > 0) {
4611cb0ef41Sopenharmony_ci    /* 4) Collect all return site value, effect, and control inputs into phis
4621cb0ef41Sopenharmony_ci     * and merges. */
4631cb0ef41Sopenharmony_ci    int const return_count = static_cast<int>(return_nodes.size());
4641cb0ef41Sopenharmony_ci    NodeVector controls(zone());
4651cb0ef41Sopenharmony_ci    NodeVector effects(zone());
4661cb0ef41Sopenharmony_ci    for (Node* const return_node : return_nodes) {
4671cb0ef41Sopenharmony_ci      controls.push_back(NodeProperties::GetControlInput(return_node));
4681cb0ef41Sopenharmony_ci      effects.push_back(NodeProperties::GetEffectInput(return_node));
4691cb0ef41Sopenharmony_ci    }
4701cb0ef41Sopenharmony_ci    Node* control_output = graph()->NewNode(common()->Merge(return_count),
4711cb0ef41Sopenharmony_ci                                            return_count, &controls.front());
4721cb0ef41Sopenharmony_ci    effects.push_back(control_output);
4731cb0ef41Sopenharmony_ci    Node* effect_output =
4741cb0ef41Sopenharmony_ci        graph()->NewNode(common()->EffectPhi(return_count),
4751cb0ef41Sopenharmony_ci                         static_cast<int>(effects.size()), &effects.front());
4761cb0ef41Sopenharmony_ci
4771cb0ef41Sopenharmony_ci    // The first input of a return node is discarded. This is because Wasm
4781cb0ef41Sopenharmony_ci    // functions always return an additional 0 constant as a first return value.
4791cb0ef41Sopenharmony_ci    DCHECK(
4801cb0ef41Sopenharmony_ci        Int32Matcher(NodeProperties::GetValueInput(return_nodes[0], 0)).Is(0));
4811cb0ef41Sopenharmony_ci    int const return_arity = return_nodes[0]->op()->ValueInputCount() - 1;
4821cb0ef41Sopenharmony_ci    NodeVector values(zone());
4831cb0ef41Sopenharmony_ci    for (int i = 0; i < return_arity; i++) {
4841cb0ef41Sopenharmony_ci      NodeVector ith_values(zone());
4851cb0ef41Sopenharmony_ci      for (Node* const return_node : return_nodes) {
4861cb0ef41Sopenharmony_ci        Node* value = NodeProperties::GetValueInput(return_node, i + 1);
4871cb0ef41Sopenharmony_ci        ith_values.push_back(value);
4881cb0ef41Sopenharmony_ci      }
4891cb0ef41Sopenharmony_ci      ith_values.push_back(control_output);
4901cb0ef41Sopenharmony_ci      // Find the correct machine representation for the return values from the
4911cb0ef41Sopenharmony_ci      // inlinee signature.
4921cb0ef41Sopenharmony_ci      MachineRepresentation repr =
4931cb0ef41Sopenharmony_ci          inlinee_sig->GetReturn(i).machine_representation();
4941cb0ef41Sopenharmony_ci      Node* ith_value_output = graph()->NewNode(
4951cb0ef41Sopenharmony_ci          common()->Phi(repr, return_count),
4961cb0ef41Sopenharmony_ci          static_cast<int>(ith_values.size()), &ith_values.front());
4971cb0ef41Sopenharmony_ci      values.push_back(ith_value_output);
4981cb0ef41Sopenharmony_ci    }
4991cb0ef41Sopenharmony_ci    for (Node* return_node : return_nodes) return_node->Kill();
5001cb0ef41Sopenharmony_ci
5011cb0ef41Sopenharmony_ci    if (return_arity == 0) {
5021cb0ef41Sopenharmony_ci      // Void function, no value uses.
5031cb0ef41Sopenharmony_ci      ReplaceWithValue(call, mcgraph()->Dead(), effect_output, control_output);
5041cb0ef41Sopenharmony_ci    } else if (return_arity == 1) {
5051cb0ef41Sopenharmony_ci      // One return value. Just replace value uses of the call node with it.
5061cb0ef41Sopenharmony_ci      ReplaceWithValue(call, values[0], effect_output, control_output);
5071cb0ef41Sopenharmony_ci    } else {
5081cb0ef41Sopenharmony_ci      // Multiple returns. We have to find the projections of the call node and
5091cb0ef41Sopenharmony_ci      // replace them with the returned values.
5101cb0ef41Sopenharmony_ci      for (Edge use_edge : call->use_edges()) {
5111cb0ef41Sopenharmony_ci        if (NodeProperties::IsValueEdge(use_edge)) {
5121cb0ef41Sopenharmony_ci          Node* use = use_edge.from();
5131cb0ef41Sopenharmony_ci          DCHECK_EQ(use->opcode(), IrOpcode::kProjection);
5141cb0ef41Sopenharmony_ci          ReplaceWithValue(use, values[ProjectionIndexOf(use->op())]);
5151cb0ef41Sopenharmony_ci        }
5161cb0ef41Sopenharmony_ci      }
5171cb0ef41Sopenharmony_ci      // All value inputs are replaced by the above loop, so it is ok to use
5181cb0ef41Sopenharmony_ci      // Dead() as a dummy for value replacement.
5191cb0ef41Sopenharmony_ci      ReplaceWithValue(call, mcgraph()->Dead(), effect_output, control_output);
5201cb0ef41Sopenharmony_ci    }
5211cb0ef41Sopenharmony_ci  } else {
5221cb0ef41Sopenharmony_ci    // The callee can never return. The call node and all its uses are dead.
5231cb0ef41Sopenharmony_ci    ReplaceWithValue(call, mcgraph()->Dead(), mcgraph()->Dead(),
5241cb0ef41Sopenharmony_ci                     mcgraph()->Dead());
5251cb0ef41Sopenharmony_ci  }
5261cb0ef41Sopenharmony_ci}
5271cb0ef41Sopenharmony_ci
5281cb0ef41Sopenharmony_ciconst wasm::WasmModule* WasmInliner::module() const { return env_->module; }
5291cb0ef41Sopenharmony_ci
5301cb0ef41Sopenharmony_ci#undef TRACE
5311cb0ef41Sopenharmony_ci
5321cb0ef41Sopenharmony_ci}  // namespace compiler
5331cb0ef41Sopenharmony_ci}  // namespace internal
5341cb0ef41Sopenharmony_ci}  // namespace v8
535