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