• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "inlining.h"
17 #include <cstddef>
18 #include "compiler_logger.h"
19 #include "compiler_options.h"
20 #include "events_gen.h"
21 #include "optimizer/ir/graph.h"
22 #include "optimizer/ir/basicblock.h"
23 #include "optimizer/ir_builder/ir_builder.h"
24 #include "optimizer/analysis/alias_analysis.h"
25 #include "optimizer/analysis/rpo.h"
26 #include "optimizer/analysis/dominators_tree.h"
27 #include "optimizer/optimizations/cleanup.h"
28 #include "optimizer/optimizations/branch_elimination.h"
29 #include "optimizer/optimizations/object_type_check_elimination.h"
30 #include "optimizer/analysis/object_type_propagation.h"
31 #include "optimizer/optimizations/optimize_string_concat.h"
32 #include "optimizer/optimizations/peepholes.h"
33 #include "optimizer/optimizations/simplify_string_builder.h"
34 #include "events/events.h"
35 
36 namespace ark::compiler {
37 using MethodPtr = RuntimeInterface::MethodPtr;
38 
39 // Explicitly instantiate both versions of CheckMethodCanBeInlined since they can be used in subclasses
40 template bool Inlining::CheckMethodCanBeInlined<false, true>(const CallInst *, InlineContext *);
41 template bool Inlining::CheckMethodCanBeInlined<true, true>(const CallInst *, InlineContext *);
42 template bool Inlining::CheckMethodCanBeInlined<false, false>(const CallInst *, InlineContext *);
43 template bool Inlining::CheckMethodCanBeInlined<true, false>(const CallInst *, InlineContext *);
44 
CanReplaceWithCallStatic(Opcode opcode)45 inline bool CanReplaceWithCallStatic(Opcode opcode)
46 {
47     switch (opcode) {
48         case Opcode::CallResolvedVirtual:
49         case Opcode::CallVirtual:
50             return true;
51         default:
52             return false;
53     }
54 }
55 
CalculateInstructionsCount(Graph * graph)56 size_t Inlining::CalculateInstructionsCount(Graph *graph)
57 {
58     size_t count = 0;
59     for (auto bb : *graph) {
60         if (bb == nullptr || bb->IsStartBlock() || bb->IsEndBlock()) {
61             continue;
62         }
63         for (auto inst : bb->Insts()) {
64             if (inst->IsSaveState() || inst->IsPhi()) {
65                 continue;
66             }
67             switch (inst->GetOpcode()) {
68                 case Opcode::Return:
69                 case Opcode::ReturnI:
70                 case Opcode::ReturnVoid:
71                     break;
72                 default:
73                     count++;
74             }
75         }
76     }
77     return count;
78 }
79 
Inlining(Graph * graph,uint32_t instructionsCount,uint32_t methodsInlined,const ArenaVector<RuntimeInterface::MethodPtr> * inlinedStack)80 Inlining::Inlining(Graph *graph, uint32_t instructionsCount, uint32_t methodsInlined,
81                    const ArenaVector<RuntimeInterface::MethodPtr> *inlinedStack)
82     : Optimization(graph),
83       methodsInlined_(methodsInlined),
84       instructionsCount_(instructionsCount != 0 ? instructionsCount : CalculateInstructionsCount(graph)),
85       instructionsLimit_(g_options.GetCompilerInliningMaxInsts()),
86       returnBlocks_(graph->GetLocalAllocator()->Adapter()),
87       blacklist_(graph->GetLocalAllocator()->Adapter()),
88       inlinedStack_(graph->GetLocalAllocator()->Adapter()),
89       vregsCount_(graph->GetVRegsCount()),
90       cha_(graph->GetRuntime()->GetCha())
91 {
92     if (inlinedStack != nullptr) {
93         inlinedStack_.reserve(inlinedStack->size() + 1U);
94         inlinedStack_ = *inlinedStack;
95     } else {
96         inlinedStack_.reserve(1U);
97     }
98     inlinedStack_.push_back(graph->GetMethod());
99 }
100 
IsInlineCachesEnabled() const101 bool Inlining::IsInlineCachesEnabled() const
102 {
103     return DoesArchSupportDeoptimization(GetGraph()->GetArch()) && !g_options.IsCompilerNoPicInlining();
104 }
105 
106 #ifdef PANDA_EVENTS_ENABLED
EmitEvent(const Graph * graph,const CallInst * callInst,const InlineContext & ctx,events::InlineResult res)107 static void EmitEvent(const Graph *graph, const CallInst *callInst, const InlineContext &ctx, events::InlineResult res)
108 {
109     auto runtime = graph->GetRuntime();
110     events::InlineKind kind;
111     if (ctx.chaDevirtualize) {
112         kind = events::InlineKind::VIRTUAL_CHA;
113     } else if (CanReplaceWithCallStatic(callInst->GetOpcode()) || ctx.replaceToStatic) {
114         kind = events::InlineKind::VIRTUAL;
115     } else {
116         kind = events::InlineKind::STATIC;
117     }
118     EVENT_INLINE(runtime->GetMethodFullName(graph->GetMethod()),
119                  ctx.method != nullptr ? runtime->GetMethodFullName(ctx.method) : "null", callInst->GetId(), kind, res);
120 }
121 #else
122 // NOLINTNEXTLINE(readability-named-parameter)
EmitEvent(const Graph *,const CallInst *,const InlineContext &,events::InlineResult)123 static void EmitEvent(const Graph *, const CallInst *, const InlineContext &, events::InlineResult) {}
124 #endif
125 
RunImpl()126 bool Inlining::RunImpl()
127 {
128     GetGraph()->RunPass<LoopAnalyzer>();
129 
130     auto blacklistNames = g_options.GetCompilerInliningBlacklist();
131     blacklist_.reserve(blacklistNames.size());
132 
133     for (const auto &methodName : blacklistNames) {
134         blacklist_.insert(methodName);
135     }
136     return Do();
137 }
138 
RunOptimizations() const139 void Inlining::RunOptimizations() const
140 {
141     if (GetGraph()->GetParentGraph() == nullptr && GetGraph()->RunPass<ObjectTypeCheckElimination>() &&
142         GetGraph()->RunPass<Peepholes>()) {
143         GetGraph()->RunPass<BranchElimination>();
144     }
145 }
146 
Do()147 bool Inlining::Do()
148 {
149     bool inlined = false;
150     RunOptimizations();
151 
152     ArenaVector<BasicBlock *> hotBlocks(GetGraph()->GetLocalAllocator()->Adapter());
153 
154     hotBlocks = GetGraph()->GetVectorBlocks();
155     if (GetGraph()->IsAotMode()) {
156         std::stable_sort(hotBlocks.begin(), hotBlocks.end(), [](BasicBlock *a, BasicBlock *b) {
157             auto ad = (a == nullptr) ? 0 : a->GetLoop()->GetDepth();
158             auto bd = (b == nullptr) ? 0 : b->GetLoop()->GetDepth();
159             return ad > bd;
160         });
161     } else {
162         std::stable_sort(hotBlocks.begin(), hotBlocks.end(), [](BasicBlock *a, BasicBlock *b) {
163             auto ahtn = (a == nullptr) ? 0 : a->GetHotness();
164             auto bhtn = (b == nullptr) ? 0 : b->GetHotness();
165             return ahtn > bhtn;
166         });
167     }
168 
169     LOG_INLINING(DEBUG) << "Process blocks (" << hotBlocks.size() << " blocks):";
170     auto lastId = hotBlocks.size();
171     for (size_t i = 0; i < lastId; i++) {
172         if (SkipBlock(hotBlocks[i])) {
173             LOG_INLINING(DEBUG) << "-skip (" << i << '/' << hotBlocks.size() << ")";
174             continue;
175         }
176 
177         [[maybe_unused]] auto hotIdx = std::find(hotBlocks.begin(), hotBlocks.end(), hotBlocks[i]) - hotBlocks.begin();
178         [[maybe_unused]] auto bbId = hotBlocks[i]->GetId();
179         LOG_INLINING(DEBUG) << "-process BB" << bbId << " (" << i << '/' << hotBlocks.size() << "): htn_"
180                             << hotBlocks[i]->GetHotness() << " (" << hotIdx << '/' << hotBlocks.size() << ")";
181 
182         for (auto inst : hotBlocks[i]->InstsSafe()) {
183             if (GetGraph()->GetVectorBlocks()[inst->GetBasicBlock()->GetId()] == nullptr) {
184                 break;
185             }
186 
187             if (!IsInstSuitableForInline(inst)) {
188                 continue;
189             }
190 
191             inlined |= TryInline(static_cast<CallInst *>(inst));
192         }
193     }
194 
195 #ifndef NDEBUG
196     GetGraph()->SetInliningComplete();
197 #endif  // NDEBUG
198 
199     return inlined;
200 }
201 
IsInstSuitableForInline(Inst * inst) const202 bool Inlining::IsInstSuitableForInline(Inst *inst) const
203 {
204     if (!inst->IsCall()) {
205         return false;
206     }
207     auto callInst = static_cast<CallInst *>(inst);
208     ASSERT(!callInst->IsDynamicCall());
209     if (callInst->IsInlined() || callInst->IsLaunchCall()) {
210         return false;
211     }
212     if (callInst->IsUnresolved() || callInst->GetCallMethod() == nullptr) {
213         LOG_INLINING(DEBUG) << "Unknown method " << callInst->GetCallMethodId();
214         return false;
215     }
216     ASSERT(callInst->GetCallMethod() != nullptr);
217     return true;
218 }
219 
InvalidateAnalyses()220 void Inlining::InvalidateAnalyses()
221 {
222     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
223     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
224     GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
225     GetGraph()->InvalidateAnalysis<ObjectTypePropagation>();
226     InvalidateBlocksOrderAnalyzes(GetGraph());
227 }
228 
229 /**
230  * Get next Parameter instruction.
231  * NOTE(msherstennikov): this is temporary solution, need to find out better approach
232  * @param inst current param instruction
233  * @return next Parameter instruction, nullptr if there no more Parameter instructions
234  */
GetNextParam(Inst * inst)235 Inst *GetNextParam(Inst *inst)
236 {
237     for (auto nextInst = inst->GetNext(); nextInst != nullptr; nextInst = nextInst->GetNext()) {
238         if (nextInst->GetOpcode() == Opcode::Parameter) {
239             return nextInst;
240         }
241     }
242     return nullptr;
243 }
244 
TryInline(CallInst * callInst)245 bool Inlining::TryInline(CallInst *callInst)
246 {
247     InlineContext ctx;
248     if (!ResolveTarget(callInst, &ctx)) {
249         if (IsInlineCachesEnabled()) {
250             return TryInlineWithInlineCaches(callInst);
251         }
252         return false;
253     }
254 
255     ASSERT(!callInst->IsInlined());
256 
257     LOG_INLINING(DEBUG) << "Try to inline(id=" << callInst->GetId() << (callInst->IsVirtualCall() ? ", virtual" : "")
258                         << ", size=" << GetGraph()->GetRuntime()->GetMethodCodeSize(ctx.method) << ", vregs="
259                         << GetGraph()->GetRuntime()->GetMethodArgumentsCount(ctx.method) +
260                                GetGraph()->GetRuntime()->GetMethodRegistersCount(ctx.method) + 1
261                         << (ctx.chaDevirtualize ? ", CHA" : "")
262                         << "): " << GetGraph()->GetRuntime()->GetMethodFullName(ctx.method, true);
263 
264     if (DoInline(callInst, &ctx)) {
265         if (IsIntrinsic(&ctx)) {
266             callInst->GetBasicBlock()->RemoveInst(callInst);
267         }
268         EmitEvent(GetGraph(), callInst, ctx, events::InlineResult::SUCCESS);
269         return true;
270     }
271     if (ctx.replaceToStatic && !ctx.chaDevirtualize) {
272         ASSERT(ctx.method != nullptr);
273         if (callInst->GetCallMethod() != ctx.method) {
274             // Replace method id only if the methods are different.
275             // Otherwise, leave the method id as is because:
276             // 1. In aot mode the new method id can refer to a method from a different file
277             // 2. In jit mode the method id does not matter
278             callInst->SetCallMethodId(GetGraph()->GetRuntime()->GetMethodId(ctx.method));
279         }
280         callInst->SetCallMethod(ctx.method);
281         if (callInst->GetOpcode() == Opcode::CallResolvedVirtual) {
282             // Drop the first argument - the resolved method
283             ASSERT(!callInst->GetInputs().empty());
284             for (size_t i = 0; i < callInst->GetInputsCount() - 1; i++) {
285                 callInst->SetInput(i, callInst->GetInput(i + 1).GetInst());
286             }
287             callInst->RemoveInput(callInst->GetInputsCount() - 1);
288             ASSERT(!callInst->GetInputTypes()->empty());
289             callInst->GetInputTypes()->erase(callInst->GetInputTypes()->begin());
290         }
291         callInst->SetOpcode(Opcode::CallStatic);
292         EmitEvent(GetGraph(), callInst, ctx, events::InlineResult::DEVIRTUALIZED);
293         return true;
294     }
295 
296     return false;
297 }
298 
TryInlineWithInlineCaches(CallInst * callInst)299 bool Inlining::TryInlineWithInlineCaches(CallInst *callInst)
300 {
301     if (GetGraph()->IsAotMode()) {
302         // We don't support offline inline caches yet.
303         return false;
304     }
305     auto runtime = GetGraph()->GetRuntime();
306     auto pic = runtime->GetInlineCaches();
307     if (pic == nullptr) {
308         return false;
309     }
310 
311     ArenaVector<RuntimeInterface::ClassPtr> receivers(GetGraph()->GetLocalAllocator()->Adapter());
312     auto callKind = pic->GetClasses(GetGraph()->GetMethod(), callInst->GetPc(), &receivers);
313     switch (callKind) {
314         case InlineCachesInterface::CallKind::MEGAMORPHIC:
315             EVENT_INLINE(runtime->GetMethodFullName(GetGraph()->GetMethod()), "-", callInst->GetId(),
316                          events::InlineKind::VIRTUAL_POLYMORPHIC, events::InlineResult::FAIL_MEGAMORPHIC);
317             return false;
318         case InlineCachesInterface::CallKind::UNKNOWN:
319             return false;
320         case InlineCachesInterface::CallKind::MONOMORPHIC:
321             return DoInlineMonomorphic(callInst, receivers[0]);
322         case InlineCachesInterface::CallKind::POLYMORPHIC:
323             ASSERT(callKind == InlineCachesInterface::CallKind::POLYMORPHIC);
324             return DoInlinePolymorphic(callInst, &receivers);
325         default:
326             break;
327     }
328     return false;
329 }
330 
DoInlineMonomorphic(CallInst * callInst,RuntimeInterface::ClassPtr receiver)331 bool Inlining::DoInlineMonomorphic(CallInst *callInst, RuntimeInterface::ClassPtr receiver)
332 {
333     auto runtime = GetGraph()->GetRuntime();
334     InlineContext ctx;
335     ctx.method = runtime->ResolveVirtualMethod(receiver, callInst->GetCallMethod());
336 
337     auto callBb = callInst->GetBasicBlock();
338     auto saveState = callInst->GetSaveState();
339     auto objInst = callInst->GetObjectInst();
340 
341     LOG_INLINING(DEBUG) << "Try to inline monomorphic(size=" << runtime->GetMethodCodeSize(ctx.method)
342                         << "): " << GetMethodFullName(GetGraph(), ctx.method);
343 
344     if (!DoInline(callInst, &ctx)) {
345         return false;
346     }
347 
348     // Add type guard
349     auto getClsInst = GetGraph()->CreateInstGetInstanceClass(DataType::REFERENCE, callInst->GetPc(), objInst);
350     auto loadClsInst = GetGraph()->CreateInstLoadImmediate(DataType::REFERENCE, callInst->GetPc(), receiver);
351     auto cmpInst = GetGraph()->CreateInstCompare(DataType::BOOL, callInst->GetPc(), getClsInst, loadClsInst,
352                                                  DataType::REFERENCE, ConditionCode::CC_NE);
353     auto deoptInst =
354         GetGraph()->CreateInstDeoptimizeIf(callInst->GetPc(), cmpInst, saveState, DeoptimizeType::INLINE_IC);
355     if (IsIntrinsic(&ctx)) {
356         callInst->InsertBefore(loadClsInst);
357         callInst->InsertBefore(getClsInst);
358         callInst->InsertBefore(cmpInst);
359         callInst->InsertBefore(deoptInst);
360         callInst->GetBasicBlock()->RemoveInst(callInst);
361     } else {
362         callBb->AppendInst(loadClsInst);
363         callBb->AppendInst(getClsInst);
364         callBb->AppendInst(cmpInst);
365         callBb->AppendInst(deoptInst);
366     }
367 
368     EVENT_INLINE(runtime->GetMethodFullName(GetGraph()->GetMethod()), runtime->GetMethodFullName(ctx.method),
369                  callInst->GetId(), events::InlineKind::VIRTUAL_MONOMORPHIC, events::InlineResult::SUCCESS);
370     return true;
371 }
372 
CreateCompareClass(CallInst * callInst,Inst * getClsInst,RuntimeInterface::ClassPtr receiver,BasicBlock * callBb)373 void Inlining::CreateCompareClass(CallInst *callInst, Inst *getClsInst, RuntimeInterface::ClassPtr receiver,
374                                   BasicBlock *callBb)
375 {
376     auto loadClsInst = GetGraph()->CreateInstLoadImmediate(DataType::REFERENCE, callInst->GetPc(), receiver);
377     auto cmpInst = GetGraph()->CreateInstCompare(DataType::BOOL, callInst->GetPc(), loadClsInst, getClsInst,
378                                                  DataType::REFERENCE, ConditionCode::CC_EQ);
379     auto ifInst = GetGraph()->CreateInstIfImm(DataType::BOOL, callInst->GetPc(), cmpInst, 0, DataType::BOOL,
380                                               ConditionCode::CC_NE);
381     callBb->AppendInst(loadClsInst);
382     callBb->AppendInst(cmpInst);
383     callBb->AppendInst(ifInst);
384 }
385 
InsertDeoptimizeInst(CallInst * callInst,BasicBlock * callBb,DeoptimizeType deoptType)386 void Inlining::InsertDeoptimizeInst(CallInst *callInst, BasicBlock *callBb, DeoptimizeType deoptType)
387 {
388     // If last class compare returns false we need to deoptimize the method.
389     // So we construct instruction DeoptimizeIf and insert instead of IfImm inst.
390     auto ifInst = callBb->GetLastInst();
391     ASSERT(ifInst != nullptr && ifInst->GetOpcode() == Opcode::IfImm);
392     ASSERT(ifInst->CastToIfImm()->GetImm() == 0 && ifInst->CastToIfImm()->GetCc() == ConditionCode::CC_NE);
393 
394     auto compareInst = ifInst->GetInput(0).GetInst()->CastToCompare();
395     ASSERT(compareInst != nullptr && compareInst->GetCc() == ConditionCode::CC_EQ);
396     compareInst->SetCc(ConditionCode::CC_NE);
397 
398     auto deoptInst =
399         GetGraph()->CreateInstDeoptimizeIf(callInst->GetPc(), compareInst, callInst->GetSaveState(), deoptType);
400 
401     callBb->RemoveInst(ifInst);
402     callBb->AppendInst(deoptInst);
403 }
404 
InsertCallInst(CallInst * callInst,BasicBlock * callBb,BasicBlock * retBb,Inst * phiInst)405 void Inlining::InsertCallInst(CallInst *callInst, BasicBlock *callBb, BasicBlock *retBb, Inst *phiInst)
406 {
407     ASSERT(phiInst == nullptr || phiInst->GetBasicBlock() == retBb);
408     // Insert new BB
409     auto newCallBb = GetGraph()->CreateEmptyBlock(callBb);
410     callBb->GetLoop()->AppendBlock(newCallBb);
411     callBb->AddSucc(newCallBb);
412     newCallBb->AddSucc(retBb);
413 
414     // Copy SaveState inst
415     auto ss = callInst->GetSaveState();
416     auto cloneSs = static_cast<SaveStateInst *>(ss->Clone(GetGraph()));
417     for (size_t inputIdx = 0; inputIdx < ss->GetInputsCount(); inputIdx++) {
418         cloneSs->AppendInput(ss->GetInput(inputIdx));
419         cloneSs->SetVirtualRegister(inputIdx, ss->GetVirtualRegister(inputIdx));
420     }
421     newCallBb->AppendInst(cloneSs);
422 
423     // Copy Call inst
424     auto cloneCall = callInst->Clone(GetGraph());
425     for (auto input : callInst->GetInputs()) {
426         cloneCall->AppendInput(input.GetInst());
427     }
428     cloneCall->SetSaveState(cloneSs);
429     newCallBb->AppendInst(cloneCall);
430 
431     // Set return value in phi inst
432     if (phiInst != nullptr) {
433         phiInst->AppendInput(cloneCall);
434     }
435 }
436 
UpdateParameterDataflow(Graph * graphInl,Inst * callInst)437 void Inlining::UpdateParameterDataflow(Graph *graphInl, Inst *callInst)
438 {
439     // Replace inlined graph incoming dataflow edges
440     auto startBb = graphInl->GetStartBlock();
441     // Last input is SaveState
442     if (callInst->GetInputsCount() > 1) {
443         Inst *paramInst = *startBb->Insts().begin();
444         if (paramInst != nullptr && paramInst->GetOpcode() != Opcode::Parameter) {
445             paramInst = GetNextParam(paramInst);
446         }
447         while (paramInst != nullptr) {
448             ASSERT(paramInst);
449             auto argNum = paramInst->CastToParameter()->GetArgNumber();
450             if (callInst->GetOpcode() == Opcode::CallResolvedVirtual ||
451                 callInst->GetOpcode() == Opcode::CallResolvedStatic) {
452                 ASSERT(argNum != ParameterInst::DYNAMIC_NUM_ARGS);
453                 argNum += 1;  // skip method_reg
454             }
455             Inst *input = nullptr;
456             if (argNum < callInst->GetInputsCount() - 1) {
457                 input = callInst->GetInput(argNum).GetInst();
458             } else if (argNum == ParameterInst::DYNAMIC_NUM_ARGS) {
459                 input = GetGraph()->FindOrCreateConstant(callInst->GetInputsCount() - 1);
460             } else {
461                 input = GetGraph()->FindOrCreateConstant(DataType::Any(coretypes::TaggedValue::VALUE_UNDEFINED));
462             }
463             paramInst->ReplaceUsers(input);
464             paramInst = GetNextParam(paramInst);
465         }
466     }
467 }
468 
UpdateExternalParameterDataflow(Graph * graphInl,Inst * callInst)469 void UpdateExternalParameterDataflow(Graph *graphInl, Inst *callInst)
470 {
471     // Replace inlined graph incoming dataflow edges
472     auto startBb = graphInl->GetStartBlock();
473     // Last input is SaveState
474     if (callInst->GetInputsCount() <= 1) {
475         return;
476     }
477     Inst *paramInst = *startBb->Insts().begin();
478     if (paramInst != nullptr && paramInst->GetOpcode() != Opcode::Parameter) {
479         paramInst = GetNextParam(paramInst);
480     }
481     ArenaVector<Inst *> worklist {graphInl->GetLocalAllocator()->Adapter()};
482     while (paramInst != nullptr) {
483         auto argNum = paramInst->CastToParameter()->GetArgNumber();
484         ASSERT(argNum != ParameterInst::DYNAMIC_NUM_ARGS);
485         if (callInst->GetOpcode() == Opcode::CallResolvedVirtual ||
486             callInst->GetOpcode() == Opcode::CallResolvedStatic) {
487             argNum += 1;  // skip method_reg
488         }
489         ASSERT(argNum < callInst->GetInputsCount() - 1);
490         auto input = callInst->GetInput(argNum);
491         for (auto &user : paramInst->GetUsers()) {
492             if (user.GetInst()->GetOpcode() == Opcode::NullCheck) {
493                 user.GetInst()->ReplaceUsers(input.GetInst());
494                 worklist.push_back(user.GetInst());
495             }
496         }
497         paramInst->ReplaceUsers(input.GetInst());
498         paramInst = GetNextParam(paramInst);
499     }
500     for (auto inst : worklist) {
501         auto ss = inst->GetInput(1).GetInst();
502         inst->RemoveInputs();
503         inst->GetBasicBlock()->EraseInst(inst);
504         ss->RemoveInputs();
505         ss->GetBasicBlock()->EraseInst(ss);
506     }
507 }
508 
CloneVirtualCallInst(CallInst * call,Graph * graph)509 static inline CallInst *CloneVirtualCallInst(CallInst *call, Graph *graph)
510 {
511     if (call->GetOpcode() == Opcode::CallVirtual) {
512         return call->Clone(graph)->CastToCallVirtual();
513     }
514     if (call->GetOpcode() == Opcode::CallResolvedVirtual) {
515         return call->Clone(graph)->CastToCallResolvedVirtual();
516     }
517     UNREACHABLE();
518 }
519 
DoInlinePolymorphic(CallInst * callInst,ArenaVector<RuntimeInterface::ClassPtr> * receivers)520 bool Inlining::DoInlinePolymorphic(CallInst *callInst, ArenaVector<RuntimeInterface::ClassPtr> *receivers)
521 {
522     LOG_INLINING(DEBUG) << "Try inline polymorphic call(" << receivers->size() << " receivers):";
523     LOG_INLINING(DEBUG) << "  instruction: " << *callInst;
524 
525     bool hasUnreachableBlocks = false;
526     bool hasRuntimeCalls = false;
527     auto runtime = GetGraph()->GetRuntime();
528     auto getClsInst = GetGraph()->CreateInstGetInstanceClass(DataType::REFERENCE, callInst->GetPc());
529     PhiInst *phiInst = nullptr;
530     BasicBlock *callBb = nullptr;
531     BasicBlock *callContBb = nullptr;
532     auto inlinedMethods = methodsInlined_;
533 
534     // For each receiver we construct BB for CallVirtual inlined, and BB for Return.Inlined
535     // Inlined graph we inserts between the blocks:
536     // BEFORE:
537     //     call_bb:
538     //         call_inst
539     //         succs [call_cont_bb]
540     //     call_cont_bb:
541     //
542     // AFTER:
543     //     call_bb:
544     //         compare_classes
545     //     succs [new_call_bb, call_inlined_block]
546     //
547     //     call_inlined_block:
548     //         call_inst.inlined
549     //     succs [inlined_graph]
550     //
551     //     inlined graph:
552     //     succs [return_inlined_block]
553     //
554     //     return_inlined_block:
555     //         return.inlined
556     //     succs [call_cont_bb]
557     //
558     //     new_call_bb:
559     //     succs [call_cont_bb]
560     //
561     //     call_cont_bb
562     //         phi(new_call_bb, return_inlined_block)
563     for (auto receiver : *receivers) {
564         InlineContext ctx;
565         ctx.method = runtime->ResolveVirtualMethod(receiver, callInst->GetCallMethod());
566         ASSERT(ctx.method != nullptr && !runtime->IsMethodAbstract(ctx.method));
567         LOG_INLINING(DEBUG) << "Inline receiver " << runtime->GetMethodFullName(ctx.method);
568         if (!CheckMethodCanBeInlined<true, false>(callInst, &ctx)) {
569             continue;
570         }
571         ASSERT(ctx.intrinsicId == RuntimeInterface::IntrinsicId::INVALID);
572 
573         // Create Call.inlined
574         CallInst *newCallInst = CloneVirtualCallInst(callInst, GetGraph());
575         newCallInst->SetCallMethodId(runtime->GetMethodId(ctx.method));
576         newCallInst->SetCallMethod(ctx.method);
577         auto inlGraph = BuildGraph(&ctx, callInst, newCallInst);
578         if (inlGraph.graph == nullptr) {
579             continue;
580         }
581         vregsCount_ += inlGraph.graph->GetVRegsCount();
582         auto blocks = MakeCallBbs({callInst, getClsInst}, {callBb, callContBb}, &phiInst, receivers->size());
583         callBb = blocks.first;
584         callContBb = blocks.second;
585 
586         CreateCompareClass(callInst, getClsInst, receiver, callBb);
587         InlineReceiver({callInst, newCallInst, phiInst}, {callBb, callContBb},
588                        {&hasUnreachableBlocks, &hasRuntimeCalls}, receivers->size(), inlGraph);
589 
590         GetGraph()->GetPassManager()->GetStatistics()->AddInlinedMethods(1);
591         EVENT_INLINE(runtime->GetMethodFullName(GetGraph()->GetMethod()), runtime->GetMethodFullName(ctx.method),
592                      callInst->GetId(), events::InlineKind::VIRTUAL_POLYMORPHIC, events::InlineResult::SUCCESS);
593         LOG_INLINING(DEBUG) << "Successfully inlined: " << GetMethodFullName(GetGraph(), ctx.method);
594         methodsInlined_++;
595     }
596 
597     bool needToDeoptimize = (methodsInlined_ - inlinedMethods == receivers->size());
598     return FinalizeInlineReceiver({callInst, getClsInst, phiInst}, {callBb, callContBb},
599                                   {&hasUnreachableBlocks, &hasRuntimeCalls}, needToDeoptimize);
600 }
601 
MakeCallBbs(InstPair insts,BasicBlockPair bbs,PhiInst ** phiInst,size_t receiversSize)602 Inlining::BasicBlockPair Inlining::MakeCallBbs(InstPair insts, BasicBlockPair bbs, [[maybe_unused]] PhiInst **phiInst,
603                                                [[maybe_unused]] size_t receiversSize)
604 {
605     [[maybe_unused]] auto *callInst = static_cast<CallInst *>(insts.first);
606     [[maybe_unused]] auto *getClsInst = insts.second;
607     auto [callBb, callContBb] = bbs;
608     if (callBb == nullptr) {
609         // Split block by call instruction
610         callBb = callInst->GetBasicBlock();
611         callContBb = callBb->SplitBlockAfterInstruction(callInst, false);
612         callBb->AppendInst(getClsInst);
613         if (callInst->GetType() != DataType::VOID) {
614             *phiInst = GetGraph()->CreateInstPhi(callInst->GetType(), callInst->GetPc());
615             (*phiInst)->ReserveInputs(receiversSize << 1U);
616             callContBb->AppendPhi(*phiInst);
617         }
618     } else {
619         auto newCallBb = GetGraph()->CreateEmptyBlock(callBb);
620         callBb->GetLoop()->AppendBlock(newCallBb);
621         callBb->AddSucc(newCallBb);
622         callBb = newCallBb;
623     }
624     return std::make_pair(callBb, callContBb);
625 }
626 
InlineReceiver(InstTriple insts,BasicBlockPair bbs,FlagPair flags,size_t receiversSize,InlinedGraph inlGraph)627 void Inlining::InlineReceiver(InstTriple insts, BasicBlockPair bbs, FlagPair flags, size_t receiversSize,
628                               InlinedGraph inlGraph)
629 {
630     auto *callInst = static_cast<CallInst *>(std::get<0>(insts));
631     auto *newCallInst = static_cast<CallInst *>(std::get<1>(insts));
632     auto *phiInst = static_cast<PhiInst *>(std::get<2>(insts));
633     auto [callBb, callContBb] = bbs;
634     auto [hasUnreachableBlocks, hasRuntimeCalls] = flags;
635     // Create call_inlined_block
636     auto callInlinedBlock = GetGraph()->CreateEmptyBlock(callBb);
637     callBb->GetLoop()->AppendBlock(callInlinedBlock);
638     callBb->AddSucc(callInlinedBlock);
639 
640     // Insert Call.inlined in call_inlined_block
641     newCallInst->AppendInput(callInst->GetObjectInst());
642     newCallInst->AppendInput(callInst->GetSaveState());
643     newCallInst->SetInlined(true);
644     newCallInst->SetFlag(inst_flags::NO_DST);
645     // Set NO_DCE flag, since some call instructions might not have one after inlining
646     newCallInst->SetFlag(inst_flags::NO_DCE);
647     callInlinedBlock->PrependInst(newCallInst);
648 
649     // Create return_inlined_block and inster PHI for non void functions
650     auto returnInlinedBlock = GetGraph()->CreateEmptyBlock(callBb);
651     callBb->GetLoop()->AppendBlock(returnInlinedBlock);
652     PhiInst *localPhiInst = nullptr;
653     if (callInst->GetType() != DataType::VOID) {
654         localPhiInst = GetGraph()->CreateInstPhi(callInst->GetType(), callInst->GetPc());
655         localPhiInst->ReserveInputs(receiversSize << 1U);
656         returnInlinedBlock->AppendPhi(localPhiInst);
657     }
658 
659     // Inlined graph between call_inlined_block and return_inlined_block
660     GetGraph()->SetMaxMarkerIdx(inlGraph.graph->GetCurrentMarkerIdx());
661     UpdateParameterDataflow(inlGraph.graph, callInst);
662     UpdateDataflow(inlGraph.graph, callInst, localPhiInst, phiInst);
663     MoveConstants(inlGraph.graph);
664     UpdateControlflow(inlGraph.graph, callInlinedBlock, returnInlinedBlock);
665 
666     if (!returnInlinedBlock->GetPredsBlocks().empty()) {
667         if (inlGraph.hasRuntimeCalls) {
668             auto inlinedReturn =
669                 GetGraph()->CreateInstReturnInlined(DataType::VOID, INVALID_PC, newCallInst->GetSaveState());
670             returnInlinedBlock->PrependInst(inlinedReturn);
671         }
672         if (callInst->GetType() != DataType::VOID) {
673             ASSERT(phiInst);
674             // clang-tidy think that phi_inst can be nullptr
675             phiInst->AppendInput(localPhiInst);  // NOLINT
676         }
677         returnInlinedBlock->AddSucc(callContBb);
678     } else {
679         // We need remove return_inlined_block if inlined graph doesn't have Return inst(only Throw or Deoptimize)
680         *hasUnreachableBlocks = true;
681     }
682 
683     if (inlGraph.hasRuntimeCalls) {
684         *hasRuntimeCalls = true;
685     } else {
686         newCallInst->GetBasicBlock()->RemoveInst(newCallInst);
687     }
688 }
689 
FinalizeInlineReceiver(InstTriple insts,BasicBlockPair bbs,FlagPair flags,bool needToDeoptimize)690 bool Inlining::FinalizeInlineReceiver(InstTriple insts, BasicBlockPair bbs, FlagPair flags, bool needToDeoptimize)
691 {
692     auto *callInst = static_cast<CallInst *>(std::get<0>(insts));
693     auto *getClsInst = std::get<1>(insts);
694     auto *phiInst = static_cast<PhiInst *>(std::get<2>(insts));
695     auto [callBb, callContBb] = bbs;
696     auto [hasUnreachableBlocks, hasRuntimeCalls] = flags;
697     if (callBb == nullptr) {
698         // Nothing was inlined
699         return false;
700     }
701     if (callContBb->GetPredsBlocks().empty() || *hasUnreachableBlocks) {
702         GetGraph()->RemoveUnreachableBlocks();
703     }
704 
705     getClsInst->SetInput(0, callInst->GetObjectInst());
706 
707     if (needToDeoptimize) {
708         InsertDeoptimizeInst(callInst, callBb);
709     } else {
710         InsertCallInst(callInst, callBb, callContBb, phiInst);
711     }
712     if (callInst->GetType() != DataType::VOID) {
713         callInst->ReplaceUsers(phiInst);
714     }
715 
716     ProcessCallReturnInstructions(callInst, callContBb, *hasRuntimeCalls);
717 
718     if (*hasRuntimeCalls) {
719         callInst->GetBasicBlock()->RemoveInst(callInst);
720     }
721     return true;
722 }
723 
724 #ifndef NDEBUG
CheckExternalGraph(Graph * graph)725 void CheckExternalGraph(Graph *graph)
726 {
727     for (auto bb : graph->GetVectorBlocks()) {
728         if (bb != nullptr) {
729             for (auto inst : bb->AllInstsSafe()) {
730                 ASSERT(!inst->RequireState());
731             }
732         }
733     }
734 }
735 #endif
736 
GetNewDefAndCorrectDF(Inst * callInst,Inst * oldDef)737 Inst *Inlining::GetNewDefAndCorrectDF(Inst *callInst, Inst *oldDef)
738 {
739     if (oldDef->IsConst()) {
740         auto constant = oldDef->CastToConstant();
741         auto exisingConstant = GetGraph()->FindOrAddConstant(constant);
742         return exisingConstant;
743     }
744     if (oldDef->IsParameter()) {
745         auto argNum = oldDef->CastToParameter()->GetArgNumber();
746         ASSERT(argNum < callInst->GetInputsCount() - 1);
747         auto input = callInst->GetInput(argNum).GetInst();
748         return input;
749     }
750     ASSERT(oldDef->GetOpcode() == Opcode::NullPtr);
751     auto exisingNullptr = GetGraph()->GetOrCreateNullPtr();
752     return exisingNullptr;
753 }
754 
TryInlineExternal(CallInst * callInst,InlineContext * ctx)755 bool Inlining::TryInlineExternal(CallInst *callInst, InlineContext *ctx)
756 {
757     if (TryInlineExternalAot(callInst, ctx)) {
758         return true;
759     }
760     // Skip external methods
761     EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::SKIP_EXTERNAL);
762     LOG_INLINING(DEBUG) << "We can't inline external method: " << GetMethodFullName(GetGraph(), ctx->method);
763     return false;
764 }
765 
766 /*
767  * External methods could be inlined only if there are no instructions requiring state.
768  * The only exception are NullChecks that check parameters and used by LoadObject/StoreObject.
769  */
CheckExternalMethodInstructions(Graph * graph,CallInst * callInst)770 bool CheckExternalMethodInstructions(Graph *graph, CallInst *callInst)
771 {
772     ArenaUnorderedSet<Inst *> suspiciousInstructions(graph->GetLocalAllocator()->Adapter());
773     for (auto bb : graph->GetVectorBlocks()) {
774         if (bb == nullptr) {
775             continue;
776         }
777         for (auto inst : bb->InstsSafe()) {
778             bool isRtCall = inst->RequireState() || inst->IsRuntimeCall();
779             auto opcode = inst->GetOpcode();
780             if (isRtCall && opcode == Opcode::NullCheck) {
781                 suspiciousInstructions.insert(inst);
782             } else if (isRtCall && opcode != Opcode::NullCheck) {
783                 return false;
784             }
785             if (opcode != Opcode::LoadObject && opcode != Opcode::StoreObject) {
786                 continue;
787             }
788             auto nc = inst->GetInput(0).GetInst();
789             if (nc->GetOpcode() == Opcode::NullCheck && nc->HasSingleUser()) {
790                 suspiciousInstructions.erase(nc);
791             }
792             // If LoadObject/StoreObject first input (i.e. object to load data from / store data to)
793             // is a method parameter and corresponding call instruction's input is either NullCheck
794             // or NewObject then the NullCheck could be removed from external method's body because
795             // the parameter is known to be not null at the time load/store will be executed.
796             // If we can't prove that the input is not-null then NullCheck could not be eliminated
797             // and a method could not be inlined.
798             auto objInput = inst->GetDataFlowInput(0);
799             if (objInput->GetOpcode() != Opcode::Parameter) {
800                 return false;
801             }
802             auto paramId = objInput->CastToParameter()->GetArgNumber() + callInst->GetObjectIndex();
803             if (callInst->GetInput(paramId).GetInst()->GetOpcode() != Opcode::NullCheck &&
804                 callInst->GetDataFlowInput(paramId)->GetOpcode() != Opcode::NewObject) {
805                 return false;
806             }
807         }
808     }
809     return suspiciousInstructions.empty();
810 }
811 
812 /*
813  * We can only inline external methods that don't have runtime calls.
814  * The only exception from this rule are methods performing LoadObject/StoreObject
815  * to/from a parameter for which we can prove that it can't be null. In that case
816  * NullChecks preceding LoadObject/StoreObject are removed from inlined graph.
817  */
TryInlineExternalAot(CallInst * callInst,InlineContext * ctx)818 bool Inlining::TryInlineExternalAot(CallInst *callInst, InlineContext *ctx)
819 {
820     // We can't guarantee without cha that runtime will use this external file.
821     if (!GetGraph()->GetAotData()->GetUseCha()) {
822         return false;
823     }
824     IrBuilderExternalInliningAnalysis bytecodeAnalysis(GetGraph(), ctx->method);
825     if (!GetGraph()->RunPass(&bytecodeAnalysis)) {
826         EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::UNSUITABLE);
827         LOG_INLINING(DEBUG) << "We can't inline external method: " << GetMethodFullName(GetGraph(), ctx->method);
828         return false;
829     }
830     auto graphInl = GetGraph()->CreateChildGraph(ctx->method);
831     graphInl->SetCurrentInstructionId(GetGraph()->GetCurrentInstructionId());
832 
833     auto stats = GetGraph()->GetPassManager()->GetStatistics();
834     auto savedPbcInstNum = stats->GetPbcInstNum();
835     if (!TryBuildGraph(*ctx, graphInl, callInst, nullptr)) {
836         stats->SetPbcInstNum(savedPbcInstNum);
837         return false;
838     }
839 
840     graphInl->RunPass<Cleanup>();
841 
842     // External method could be inlined only if there are no instructions requiring state
843     // because compiler saves method id into stack map's inline info and there is no way
844     // to distinguish id of an external method from id of some method from the same translation unit.
845     // Following check ensures that there are no instructions requiring state within parsed
846     // external method except NullChecks used by LoadObject/StoreObject that checks nullness
847     // of parameters that known to be non-null at the call time. In that case NullChecks
848     // will be eliminated and there will no instruction requiring state.
849     if (!CheckExternalMethodInstructions(graphInl, callInst)) {
850         stats->SetPbcInstNum(savedPbcInstNum);
851         return false;
852     }
853 
854     vregsCount_ += graphInl->GetVRegsCount();
855 
856     auto method = ctx->method;
857     auto runtime = GetGraph()->GetRuntime();
858     // Call instruction is already inlined, so change its call id to the resolved method.
859     callInst->SetCallMethodId(runtime->GetMethodId(method));
860     callInst->SetCallMethod(method);
861 
862     auto callBb = callInst->GetBasicBlock();
863     auto callContBb = callBb->SplitBlockAfterInstruction(callInst, false);
864 
865     GetGraph()->SetMaxMarkerIdx(graphInl->GetCurrentMarkerIdx());
866     // Adjust instruction id counter for parent graph, thereby avoid situation when two instructions have same id.
867     GetGraph()->SetCurrentInstructionId(graphInl->GetCurrentInstructionId());
868 
869     UpdateExternalParameterDataflow(graphInl, callInst);
870     UpdateDataflow(graphInl, callInst, callContBb);
871     MoveConstants(graphInl);
872     UpdateControlflow(graphInl, callBb, callContBb);
873 
874     if (callContBb->GetPredsBlocks().empty()) {
875         GetGraph()->RemoveUnreachableBlocks();
876     } else {
877         returnBlocks_.push_back(callContBb);
878     }
879 
880     bool needBarriers = runtime->IsMemoryBarrierRequired(method);
881     ProcessCallReturnInstructions(callInst, callContBb, false, needBarriers);
882 
883 #ifndef NDEBUG
884     CheckExternalGraph(graphInl);
885 #endif
886 
887     LOG_INLINING(DEBUG) << "Successfully inlined external method: " << GetMethodFullName(GetGraph(), ctx->method);
888     methodsInlined_++;
889     return true;
890 }
891 
DoInlineIntrinsic(CallInst * callInst,InlineContext * ctx)892 bool Inlining::DoInlineIntrinsic(CallInst *callInst, InlineContext *ctx)
893 {
894     auto intrinsicId = ctx->intrinsicId;
895     ASSERT(intrinsicId != RuntimeInterface::IntrinsicId::INVALID);
896     ASSERT(callInst != nullptr);
897     if (!EncodesBuiltin(GetGraph()->GetRuntime(), intrinsicId, GetGraph()->GetArch())) {
898         return false;
899     }
900     IntrinsicInst *inst = GetGraph()->CreateInstIntrinsic(callInst->GetType(), callInst->GetPc(), intrinsicId);
901     bool needSaveState = inst->RequireState();
902 
903     size_t inputsCount = callInst->GetInputsCount() - (needSaveState ? 0 : 1);
904 
905     inst->ReserveInputs(inputsCount);
906     inst->AllocateInputTypes(GetGraph()->GetAllocator(), inputsCount);
907 
908     auto inputs = callInst->GetInputs();
909     for (size_t i = 0; i < inputsCount; ++i) {
910         inst->AppendInput(inputs[i].GetInst(), callInst->GetInputType(i));
911     }
912 
913     auto method = ctx->method;
914     if (ctx->chaDevirtualize) {
915         InsertChaGuard(callInst);
916         GetCha()->AddDependency(method, GetGraph()->GetOutermostParentGraph()->GetMethod());
917         GetGraph()->GetOutermostParentGraph()->AddSingleImplementationMethod(method);
918     }
919 
920     callInst->InsertAfter(inst);
921     callInst->ReplaceUsers(inst);
922     LOG_INLINING(DEBUG) << "The method: " << GetMethodFullName(GetGraph(), method) << "replaced to the intrinsic"
923                         << GetIntrinsicName(intrinsicId);
924 
925     return true;
926 }
927 
DoInlineMethod(CallInst * callInst,InlineContext * ctx)928 bool Inlining::DoInlineMethod(CallInst *callInst, InlineContext *ctx)
929 {
930     ASSERT(ctx->intrinsicId == RuntimeInterface::IntrinsicId::INVALID);
931     auto method = ctx->method;
932 
933     auto runtime = GetGraph()->GetRuntime();
934 
935     if (resolveWoInline_) {
936         // Return, don't inline anything
937         // At this point we:
938         // 1. Gave a chance to inline external method
939         // 2. Set replace_to_static to true where possible
940         return false;
941     }
942 
943     ASSERT(!runtime->IsMethodAbstract(method));
944 
945     // Split block by call instruction
946     auto callBb = callInst->GetBasicBlock();
947     // NOTE (a.popov) Support inlining to the catch blocks
948     if (callBb->IsCatch()) {
949         return false;
950     }
951 
952     auto graphInl = BuildGraph(ctx, callInst);
953     if (graphInl.graph == nullptr) {
954         return false;
955     }
956 
957     vregsCount_ += graphInl.graph->GetVRegsCount();
958 
959     // Call instruction is already inlined, so change its call id to the resolved method.
960     callInst->SetCallMethodId(runtime->GetMethodId(method));
961     callInst->SetCallMethod(method);
962 
963     auto callContBb = callBb->SplitBlockAfterInstruction(callInst, false);
964 
965     GetGraph()->SetMaxMarkerIdx(graphInl.graph->GetCurrentMarkerIdx());
966     UpdateParameterDataflow(graphInl.graph, callInst);
967     UpdateDataflow(graphInl.graph, callInst, callContBb);
968 
969     MoveConstants(graphInl.graph);
970 
971     UpdateControlflow(graphInl.graph, callBb, callContBb);
972 
973     if (callContBb->GetPredsBlocks().empty()) {
974         GetGraph()->RemoveUnreachableBlocks();
975     } else {
976         returnBlocks_.push_back(callContBb);
977     }
978 
979     if (ctx->chaDevirtualize) {
980         InsertChaGuard(callInst);
981         GetCha()->AddDependency(method, GetGraph()->GetOutermostParentGraph()->GetMethod());
982         GetGraph()->GetOutermostParentGraph()->AddSingleImplementationMethod(method);
983     }
984 
985     bool needBarriers = runtime->IsMemoryBarrierRequired(method);
986     ProcessCallReturnInstructions(callInst, callContBb, graphInl.hasRuntimeCalls, needBarriers);
987 
988     LOG_INLINING(DEBUG) << "Successfully inlined: " << GetMethodFullName(GetGraph(), method);
989     GetGraph()->GetPassManager()->GetStatistics()->AddInlinedMethods(1);
990     methodsInlined_++;
991 
992     return true;
993 }
994 
DoInline(CallInst * callInst,InlineContext * ctx)995 bool Inlining::DoInline(CallInst *callInst, InlineContext *ctx)
996 {
997     ASSERT(!callInst->IsInlined());
998 
999     auto method = ctx->method;
1000 
1001     auto runtime = GetGraph()->GetRuntime();
1002 
1003     if (!CheckMethodCanBeInlined<false, true>(callInst, ctx)) {
1004         return false;
1005     }
1006     if (runtime->IsMethodExternal(GetGraph()->GetMethod(), method) && !IsIntrinsic(ctx)) {
1007         if (!g_options.IsCompilerInlineExternalMethods()) {
1008             // Skip external methods
1009             EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::SKIP_EXTERNAL);
1010             LOG_INLINING(DEBUG) << "We can't inline external method: " << GetMethodFullName(GetGraph(), ctx->method);
1011             return false;
1012         }
1013         if (GetGraph()->IsAotMode()) {
1014             return TryInlineExternal(callInst, ctx);
1015         }
1016     }
1017 
1018     if (IsIntrinsic(ctx)) {
1019         return DoInlineIntrinsic(callInst, ctx);
1020     }
1021     return DoInlineMethod(callInst, ctx);
1022 }
1023 
ProcessCallReturnInstructions(CallInst * callInst,BasicBlock * callContBb,bool hasRuntimeCalls,bool needBarriers)1024 void Inlining::ProcessCallReturnInstructions(CallInst *callInst, BasicBlock *callContBb, bool hasRuntimeCalls,
1025                                              bool needBarriers)
1026 {
1027     if (hasRuntimeCalls || needBarriers) {
1028         // In case if inlined graph contains call to runtime we need to preserve call instruction with special `Inlined`
1029         // flag and create new `ReturnInlined` instruction, hereby codegen can properly handle method frames.
1030         callInst->SetInlined(true);
1031         callInst->SetFlag(inst_flags::NO_DST);
1032         // Set NO_DCE flag, since some call instructions might not have one after inlining
1033         callInst->SetFlag(inst_flags::NO_DCE);
1034         // Remove callInst's all inputs except SaveState and NullCheck(if exist)
1035         // Do not remove function (first) input for dynamic calls
1036         auto saveState = callInst->GetSaveState();
1037         ASSERT(saveState->GetOpcode() == Opcode::SaveState);
1038         if (callInst->GetOpcode() != Opcode::CallDynamic) {
1039             auto nullcheckInst = callInst->GetObjectInst();
1040             callInst->RemoveInputs();
1041             if (nullcheckInst->GetOpcode() == Opcode::NullCheck) {
1042                 callInst->AppendInput(nullcheckInst);
1043             }
1044         } else {
1045             auto func = callInst->GetInput(0).GetInst();
1046             callInst->RemoveInputs();
1047             callInst->AppendInput(func);
1048         }
1049         callInst->AppendInput(saveState);
1050         callInst->SetType(DataType::VOID);
1051         for (auto bb : returnBlocks_) {
1052             auto inlinedReturn =
1053                 GetGraph()->CreateInstReturnInlined(DataType::VOID, INVALID_PC, callInst->GetSaveState());
1054             if (bb != callContBb && (bb->IsEndWithThrowOrDeoptimize() ||
1055                                      (bb->IsEmpty() && bb->GetPredsBlocks()[0]->IsEndWithThrowOrDeoptimize()))) {
1056                 auto lastInst = !bb->IsEmpty() ? bb->GetLastInst() : bb->GetPredsBlocks()[0]->GetLastInst();
1057                 lastInst->InsertBefore(inlinedReturn);
1058                 inlinedReturn->SetExtendedLiveness();
1059             } else {
1060                 bb->PrependInst(inlinedReturn);
1061             }
1062             if (needBarriers) {
1063                 inlinedReturn->SetFlag(inst_flags::MEM_BARRIER);
1064             }
1065         }
1066     } else {
1067         // Otherwise we remove call instruction
1068         auto saveState = callInst->GetSaveState();
1069         // Remove SaveState if it has only Call instruction in the users
1070         if (saveState->GetUsers().Front().GetNext() == nullptr) {
1071             saveState->GetBasicBlock()->RemoveInst(saveState);
1072         }
1073         callInst->GetBasicBlock()->RemoveInst(callInst);
1074     }
1075     returnBlocks_.clear();
1076 }
1077 
CheckBytecode(CallInst * callInst,const InlineContext & ctx,bool * calleeCallRuntime)1078 bool Inlining::CheckBytecode(CallInst *callInst, const InlineContext &ctx, bool *calleeCallRuntime)
1079 {
1080     auto vregsNum = GetGraph()->GetRuntime()->GetMethodArgumentsCount(ctx.method) +
1081                     GetGraph()->GetRuntime()->GetMethodRegistersCount(ctx.method) + 1;
1082     if ((vregsCount_ + vregsNum) >= g_options.GetCompilerMaxVregsNum()) {
1083         EmitEvent(GetGraph(), callInst, ctx, events::InlineResult::LIMIT);
1084         LOG_INLINING(DEBUG) << "Reached vregs limit: current=" << vregsCount_ << ", inlined=" << vregsNum;
1085         return false;
1086     }
1087     IrBuilderInliningAnalysis bytecodeAnalysis(GetGraph(), ctx.method);
1088     if (!GetGraph()->RunPass(&bytecodeAnalysis)) {
1089         EmitEvent(GetGraph(), callInst, ctx, events::InlineResult::UNSUITABLE);
1090         LOG_INLINING(DEBUG) << "Method contains unsuitable bytecode";
1091         return false;
1092     }
1093 
1094     if (bytecodeAnalysis.HasRuntimeCalls() && g_options.IsCompilerInlineSimpleOnly()) {
1095         EmitEvent(GetGraph(), callInst, ctx, events::InlineResult::UNSUITABLE);
1096         return false;
1097     }
1098     if (calleeCallRuntime != nullptr) {
1099         *calleeCallRuntime = bytecodeAnalysis.HasRuntimeCalls();
1100     }
1101     return true;
1102 }
1103 
TryBuildGraph(const InlineContext & ctx,Graph * graphInl,CallInst * callInst,CallInst * polyCallInst)1104 bool Inlining::TryBuildGraph(const InlineContext &ctx, Graph *graphInl, CallInst *callInst, CallInst *polyCallInst)
1105 {
1106     if (!graphInl->RunPass<IrBuilder>(ctx.method, polyCallInst != nullptr ? polyCallInst : callInst,
1107                                       GetCurrentDepth() + 1)) {
1108         EmitEvent(GetGraph(), callInst, ctx, events::InlineResult::FAIL);
1109         LOG_INLINING(WARNING) << "Graph building failed";
1110         return false;
1111     }
1112 
1113     if (graphInl->HasInfiniteLoop()) {
1114         EmitEvent(GetGraph(), callInst, ctx, events::InlineResult::INF_LOOP);
1115         COMPILER_LOG(INFO, INLINING) << "Inlining of the methods with infinite loop is not supported";
1116         return false;
1117     }
1118 
1119     if (!g_options.IsCompilerInliningSkipAlwaysThrowMethods()) {
1120         return true;
1121     }
1122 
1123     bool alwaysThrow = true;
1124     // check that end block could be reached only through throw-blocks
1125     for (auto pred : graphInl->GetEndBlock()->GetPredsBlocks()) {
1126         auto returnInst = pred->GetLastInst();
1127         if (returnInst == nullptr) {
1128             ASSERT(pred->IsTryEnd());
1129             ASSERT(pred->GetPredsBlocks().size() == 1);
1130             pred = pred->GetPredBlockByIndex(0);
1131         }
1132         if (!pred->IsEndWithThrowOrDeoptimize()) {
1133             alwaysThrow = false;
1134             break;
1135         }
1136     }
1137     if (!alwaysThrow) {
1138         return true;
1139     }
1140     EmitEvent(GetGraph(), callInst, ctx, events::InlineResult::UNSUITABLE);
1141     LOG_INLINING(DEBUG) << "Method always throw an expection, skip inlining: "
1142                         << GetMethodFullName(GetGraph(), ctx.method);
1143     return false;
1144 }
1145 
RemoveDeadSafePoints(Graph * graphInl)1146 void RemoveDeadSafePoints(Graph *graphInl)
1147 {
1148     for (auto bb : *graphInl) {
1149         if (bb == nullptr || bb->IsStartBlock() || bb->IsEndBlock()) {
1150             continue;
1151         }
1152         for (auto inst : bb->InstsSafe()) {
1153             if (!inst->IsSaveState()) {
1154                 continue;
1155             }
1156             ASSERT(inst->GetOpcode() == Opcode::SafePoint || inst->GetOpcode() == Opcode::SaveStateDeoptimize);
1157             ASSERT(inst->GetUsers().Empty());
1158             bb->RemoveInst(inst);
1159         }
1160     }
1161 }
1162 
CheckLoops(bool * calleeCallRuntime,Graph * graphInl)1163 bool Inlining::CheckLoops(bool *calleeCallRuntime, Graph *graphInl)
1164 {
1165     // Check that inlined graph hasn't loops
1166     graphInl->RunPass<LoopAnalyzer>();
1167     if (graphInl->HasLoop()) {
1168         if (g_options.IsCompilerInlineSimpleOnly()) {
1169             LOG_INLINING(INFO) << "Inlining of the methods with loops is disabled";
1170             return false;
1171         }
1172         *calleeCallRuntime = true;
1173     } else if (!*calleeCallRuntime) {
1174         RemoveDeadSafePoints(graphInl);
1175     }
1176     return true;
1177 }
1178 
1179 /* static */
PropagateObjectInfo(Graph * graphInl,CallInst * callInst)1180 void Inlining::PropagateObjectInfo(Graph *graphInl, CallInst *callInst)
1181 {
1182     // Propagate object type information to the parameters of the inlined graph
1183     auto index = callInst->GetObjectIndex();
1184     // NOLINTNEXTLINE(readability-static-accessed-through-instance)
1185     for (auto paramInst : graphInl->GetParameters()) {
1186         auto inputInst = callInst->GetDataFlowInput(index);
1187         paramInst->SetObjectTypeInfo(inputInst->GetObjectTypeInfo());
1188         index++;
1189     }
1190 }
1191 
BuildGraph(InlineContext * ctx,CallInst * callInst,CallInst * polyCallInst)1192 InlinedGraph Inlining::BuildGraph(InlineContext *ctx, CallInst *callInst, CallInst *polyCallInst)
1193 {
1194     bool calleeCallRuntime = false;
1195     if (!CheckBytecode(callInst, *ctx, &calleeCallRuntime)) {
1196         return InlinedGraph();
1197     }
1198 
1199     auto graphInl = GetGraph()->CreateChildGraph(ctx->method);
1200 
1201     // Propagate instruction id counter to inlined graph, thereby avoid instructions id duplication
1202     graphInl->SetCurrentInstructionId(GetGraph()->GetCurrentInstructionId());
1203 
1204     auto stats = GetGraph()->GetPassManager()->GetStatistics();
1205     auto savedPbcInstNum = stats->GetPbcInstNum();
1206     if (!TryBuildGraph(*ctx, graphInl, callInst, polyCallInst)) {
1207         stats->SetPbcInstNum(savedPbcInstNum);
1208         return InlinedGraph();
1209     }
1210 
1211     PropagateObjectInfo(graphInl, callInst);
1212 
1213     // Run basic optimizations
1214     graphInl->RunPass<Cleanup>(false);
1215     auto peepholeApplied = graphInl->RunPass<Peepholes>();
1216     auto objectTypeApplied = graphInl->RunPass<ObjectTypeCheckElimination>();
1217     if (peepholeApplied || objectTypeApplied) {
1218         graphInl->RunPass<BranchElimination>();
1219     }
1220     graphInl->RunPass<Cleanup>(false);
1221     graphInl->RunPass<OptimizeStringConcat>();
1222     graphInl->RunPass<SimplifyStringBuilder>();
1223 
1224     auto inlinedInstsCount = CalculateInstructionsCount(graphInl);
1225     LOG_INLINING(DEBUG) << "Actual insts-bc ratio: (" << inlinedInstsCount << " insts) / ("
1226                         << GetGraph()->GetRuntime()->GetMethodCodeSize(ctx->method) << ") = "
1227                         << (double)inlinedInstsCount / GetGraph()->GetRuntime()->GetMethodCodeSize(ctx->method);
1228 
1229     graphInl->RunPass<Inlining>(instructionsCount_ + inlinedInstsCount, methodsInlined_ + 1, &inlinedStack_);
1230 
1231     instructionsCount_ += CalculateInstructionsCount(graphInl);
1232 
1233     GetGraph()->SetMaxMarkerIdx(graphInl->GetCurrentMarkerIdx());
1234 
1235     // Adjust instruction id counter for parent graph, thereby avoid situation when two instructions have same id.
1236     GetGraph()->SetCurrentInstructionId(graphInl->GetCurrentInstructionId());
1237 
1238     if (ctx->chaDevirtualize && !GetCha()->IsSingleImplementation(ctx->method)) {
1239         EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::LOST_SINGLE_IMPL);
1240         LOG_INLINING(WARNING) << "Method lost single implementation property while we build IR for it";
1241         stats->SetPbcInstNum(savedPbcInstNum);
1242         return InlinedGraph();
1243     }
1244 
1245     if (!CheckLoops(&calleeCallRuntime, graphInl)) {
1246         stats->SetPbcInstNum(savedPbcInstNum);
1247         return InlinedGraph();
1248     }
1249     return {graphInl, calleeCallRuntime};
1250 }
1251 
1252 template <bool CHECK_EXTERNAL>
CheckMethodSize(const CallInst * callInst,InlineContext * ctx)1253 bool Inlining::CheckMethodSize(const CallInst *callInst, InlineContext *ctx)
1254 {
1255     size_t methodSize = GetGraph()->GetRuntime()->GetMethodCodeSize(ctx->method);
1256     size_t expectedInlinedInstsCount = g_options.GetCompilerInliningInstsBcRatio() * methodSize;
1257     bool methodIsTooBig = (expectedInlinedInstsCount + instructionsCount_) > instructionsLimit_;
1258     methodIsTooBig |= methodSize >= g_options.GetCompilerInliningMaxBcSize();
1259     if (methodIsTooBig) {
1260         if (methodSize <= g_options.GetCompilerInliningAlwaysInlineBcSize()) {
1261             methodIsTooBig = false;
1262             EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::IGNORE_LIMIT);
1263             LOG_INLINING(DEBUG) << "Ignore instructions limit: ";
1264         } else {
1265             EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::LIMIT);
1266             LOG_INLINING(DEBUG) << "Method is too big (d_" << inlinedStack_.size() << "):";
1267         }
1268         LOG_INLINING(DEBUG) << "instructions_count_ = " << instructionsCount_
1269                             << ", expected_inlined_insts_count = " << expectedInlinedInstsCount
1270                             << ", instructions_limit_ = " << instructionsLimit_
1271                             << ", (method = " << GetMethodFullName(GetGraph(), ctx->method) << ")";
1272     }
1273 
1274     if (methodIsTooBig || resolveWoInline_) {
1275         return CheckTooBigMethodCanBeInlined<CHECK_EXTERNAL>(callInst, ctx, methodIsTooBig);
1276     }
1277     return true;
1278 }
1279 
1280 template <bool CHECK_EXTERNAL>
CheckTooBigMethodCanBeInlined(const CallInst * callInst,InlineContext * ctx,bool methodIsTooBig)1281 bool Inlining::CheckTooBigMethodCanBeInlined(const CallInst *callInst, InlineContext *ctx, bool methodIsTooBig)
1282 {
1283     ctx->replaceToStatic = CanReplaceWithCallStatic(callInst->GetOpcode());
1284     if constexpr (!CHECK_EXTERNAL) {
1285         if (GetGraph()->GetRuntime()->IsMethodExternal(GetGraph()->GetMethod(), ctx->method)) {
1286             // Do not replace to call static if --compiler-inline-external-methods=false
1287             ctx->replaceToStatic &= g_options.IsCompilerInlineExternalMethods();
1288             ASSERT(ctx->method != nullptr);
1289             // Allow to replace CallVirtual with CallStatic if the resolved method is same as the called method
1290             // In AOT mode the resolved method id can be different from the method id in the callInst,
1291             // but we'll keep the method id from the callInst because the resolved method id can be not correct
1292             // for aot compiled method
1293             ctx->replaceToStatic &= ctx->method == callInst->GetCallMethod()
1294                                     // Or if it's not aot mode. That is, just replace in other modes
1295                                     || !GetGraph()->IsAotMode();
1296         }
1297     }
1298     if (methodIsTooBig) {
1299         return false;
1300     }
1301     ASSERT(resolveWoInline_);
1302     // Continue and return true to give a change to TryInlineExternalAot
1303     return true;
1304 }
1305 
CheckDepthLimit(InlineContext * ctx)1306 bool Inlining::CheckDepthLimit(InlineContext *ctx)
1307 {
1308     size_t recInlinedCount = std::count(inlinedStack_.begin(), inlinedStack_.end(), ctx->method);
1309     if ((recInlinedCount >= g_options.GetCompilerInliningRecursiveCallsLimit()) ||
1310         (inlinedStack_.size() >= MAX_CALL_DEPTH)) {
1311         LOG_INLINING(DEBUG) << "Recursive-calls-depth limit reached, method: '"
1312                             << GetMethodFullName(GetGraph(), ctx->method) << "', depth: " << recInlinedCount;
1313         return false;
1314     }
1315     bool isDepthLimitIgnored = GetCurrentDepth() >= g_options.GetCompilerInliningMaxDepth();
1316     bool isSmallMethod =
1317         GetGraph()->GetRuntime()->GetMethodCodeSize(ctx->method) <= g_options.GetCompilerInliningAlwaysInlineBcSize();
1318     if (isDepthLimitIgnored && !isSmallMethod) {
1319         LOG_INLINING(DEBUG) << "Small-method-depth limit reached, method: '"
1320                             << GetMethodFullName(GetGraph(), ctx->method)
1321                             << "', size: " << GetGraph()->GetRuntime()->GetMethodCodeSize(ctx->method);
1322         return false;
1323     }
1324     return true;
1325 }
1326 
1327 template <bool CHECK_EXTERNAL, bool CHECK_INTRINSICS>
CheckMethodCanBeInlined(const CallInst * callInst,InlineContext * ctx)1328 bool Inlining::CheckMethodCanBeInlined(const CallInst *callInst, InlineContext *ctx)
1329 {
1330     if (ctx->method == nullptr) {
1331         return false;
1332     }
1333 
1334     if (!CheckDepthLimit(ctx)) {
1335         return false;
1336     }
1337 
1338     if constexpr (CHECK_EXTERNAL) {
1339         ASSERT(!GetGraph()->IsAotMode());
1340         if (!g_options.IsCompilerInlineExternalMethods() &&
1341             GetGraph()->GetRuntime()->IsMethodExternal(GetGraph()->GetMethod(), ctx->method)) {
1342             // Skip external methods
1343             EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::SKIP_EXTERNAL);
1344             LOG_INLINING(DEBUG) << "We can't inline external method: " << GetMethodFullName(GetGraph(), ctx->method);
1345             return false;
1346         }
1347     }
1348 
1349     if (!blacklist_.empty()) {
1350         std::string methodName = GetGraph()->GetRuntime()->GetMethodFullName(ctx->method);
1351         if (blacklist_.find(methodName) != blacklist_.end()) {
1352             EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::NOINLINE);
1353             LOG_INLINING(DEBUG) << "Method is in the blacklist: " << GetMethodFullName(GetGraph(), ctx->method);
1354             return false;
1355         }
1356     }
1357 
1358     if (!GetGraph()->GetRuntime()->IsMethodCanBeInlined(ctx->method)) {
1359         if constexpr (CHECK_INTRINSICS) {
1360             if (GetGraph()->GetRuntime()->IsMethodIntrinsic(ctx->method)) {
1361                 ctx->intrinsicId = GetGraph()->GetRuntime()->GetIntrinsicId(ctx->method);
1362                 return true;
1363             }
1364         }
1365         EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::UNSUITABLE);
1366         return false;
1367     }
1368 
1369     if (GetGraph()->GetRuntime()->GetMethodName(ctx->method).find("__noinline__") != std::string::npos) {
1370         EmitEvent(GetGraph(), callInst, *ctx, events::InlineResult::NOINLINE);
1371         return false;
1372     }
1373     return CheckMethodSize<CHECK_EXTERNAL>(callInst, ctx);
1374 }
1375 
RemoveReturnVoidInst(BasicBlock * endBlock)1376 void RemoveReturnVoidInst(BasicBlock *endBlock)
1377 {
1378     for (auto &pred : endBlock->GetPredsBlocks()) {
1379         auto returnInst = pred->GetLastInst();
1380         if (returnInst->GetOpcode() == Opcode::Throw || returnInst->GetOpcode() == Opcode::Deoptimize) {
1381             continue;
1382         }
1383         ASSERT(returnInst->GetOpcode() == Opcode::ReturnVoid);
1384         pred->RemoveInst(returnInst);
1385     }
1386 }
1387 
1388 /// Embed inlined dataflow graph into the caller graph. A special case where the graph is empty
UpdateDataflowForEmptyGraph(Inst * callInst,std::variant<BasicBlock *,PhiInst * > use,BasicBlock * endBlock)1389 void Inlining::UpdateDataflowForEmptyGraph(Inst *callInst, std::variant<BasicBlock *, PhiInst *> use,
1390                                            BasicBlock *endBlock)
1391 {
1392     auto predBlock = endBlock->GetPredsBlocks().front();
1393     auto returnInst = predBlock->GetLastInst();
1394     ASSERT(returnInst->GetOpcode() == Opcode::Return || returnInst->GetOpcode() == Opcode::ReturnVoid ||
1395            predBlock->IsEndWithThrowOrDeoptimize());
1396     if (returnInst->GetOpcode() == Opcode::Return) {
1397         ASSERT(returnInst->GetInputsCount() == 1);
1398         auto inputInst = returnInst->GetInput(0).GetInst();
1399         if (std::holds_alternative<PhiInst *>(use)) {
1400             auto phiInst = std::get<PhiInst *>(use);
1401             phiInst->AppendInput(inputInst);
1402         } else {
1403             callInst->ReplaceUsers(inputInst);
1404         }
1405     }
1406     if (!predBlock->IsEndWithThrowOrDeoptimize()) {
1407         predBlock->RemoveInst(returnInst);
1408     }
1409 }
1410 
1411 /// Embed inlined dataflow graph into the caller graph.
UpdateDataflow(Graph * graphInl,Inst * callInst,std::variant<BasicBlock *,PhiInst * > use,Inst * newDef)1412 void Inlining::UpdateDataflow(Graph *graphInl, Inst *callInst, std::variant<BasicBlock *, PhiInst *> use, Inst *newDef)
1413 {
1414     // Replace inlined graph outcoming dataflow edges
1415     auto endBlock = graphInl->GetEndBlock();
1416     if (endBlock->GetPredsBlocks().size() > 1) {
1417         if (callInst->GetType() == DataType::VOID) {
1418             RemoveReturnVoidInst(endBlock);
1419             return;
1420         }
1421         PhiInst *phiInst = nullptr;
1422         if (std::holds_alternative<BasicBlock *>(use)) {
1423             phiInst = GetGraph()->CreateInstPhi(GetGraph()->GetRuntime()->GetMethodReturnType(graphInl->GetMethod()),
1424                                                 INVALID_PC);
1425             phiInst->ReserveInputs(endBlock->GetPredsBlocks().size());
1426             std::get<BasicBlock *>(use)->AppendPhi(phiInst);
1427         } else {
1428             phiInst = std::get<PhiInst *>(use);
1429             ASSERT(phiInst != nullptr);
1430         }
1431         for (auto pred : endBlock->GetPredsBlocks()) {
1432             auto returnInst = pred->GetLastInst();
1433             if (returnInst == nullptr) {
1434                 ASSERT(pred->IsTryEnd());
1435                 ASSERT(pred->GetPredsBlocks().size() == 1);
1436                 pred = pred->GetPredBlockByIndex(0);
1437                 returnInst = pred->GetLastInst();
1438             }
1439             if (pred->IsEndWithThrowOrDeoptimize()) {
1440                 continue;
1441             }
1442             ASSERT(returnInst->GetOpcode() == Opcode::Return);
1443             ASSERT(returnInst->GetInputsCount() == 1);
1444             phiInst->AppendInput(returnInst->GetInput(0).GetInst());
1445             pred->RemoveInst(returnInst);
1446         }
1447         if (newDef == nullptr) {
1448             newDef = phiInst;
1449         }
1450         callInst->ReplaceUsers(newDef);
1451     } else {
1452         UpdateDataflowForEmptyGraph(callInst, use, endBlock);
1453     }
1454 }
1455 
1456 /// Embed inlined controlflow graph into the caller graph.
UpdateControlflow(Graph * graphInl,BasicBlock * callBb,BasicBlock * callContBb)1457 void Inlining::UpdateControlflow(Graph *graphInl, BasicBlock *callBb, BasicBlock *callContBb)
1458 {
1459     // Move all blocks from inlined graph to parent
1460     auto currentLoop = callBb->GetLoop();
1461     for (auto bb : graphInl->GetVectorBlocks()) {
1462         if (bb != nullptr && !bb->IsStartBlock() && !bb->IsEndBlock()) {
1463             bb->ClearMarkers();
1464             GetGraph()->AddBlock(bb);
1465             bb->CopyTryCatchProps(callBb);
1466         }
1467     }
1468     callContBb->CopyTryCatchProps(callBb);
1469 
1470     // Fix loop tree
1471     for (auto loop : graphInl->GetRootLoop()->GetInnerLoops()) {
1472         currentLoop->AppendInnerLoop(loop);
1473         loop->SetOuterLoop(currentLoop);
1474     }
1475     for (auto bb : graphInl->GetRootLoop()->GetBlocks()) {
1476         bb->SetLoop(currentLoop);
1477         currentLoop->AppendBlock(bb);
1478     }
1479 
1480     // Connect inlined graph as successor of the first part of call continuation block
1481     auto startBb = graphInl->GetStartBlock();
1482     ASSERT(startBb->GetSuccsBlocks().size() == 1);
1483     auto succ = startBb->GetSuccessor(0);
1484     succ->ReplacePred(startBb, callBb);
1485     startBb->GetSuccsBlocks().clear();
1486 
1487     ASSERT(graphInl->HasEndBlock());
1488     auto endBlock = graphInl->GetEndBlock();
1489     for (auto pred : endBlock->GetPredsBlocks()) {
1490         endBlock->RemovePred(pred);
1491         if (pred->IsEndWithThrowOrDeoptimize() ||
1492             (pred->IsEmpty() && pred->GetPredsBlocks()[0]->IsEndWithThrowOrDeoptimize())) {
1493             if (!GetGraph()->HasEndBlock()) {
1494                 GetGraph()->CreateEndBlock();
1495             }
1496             returnBlocks_.push_back(pred);
1497             pred->ReplaceSucc(endBlock, GetGraph()->GetEndBlock());
1498         } else {
1499             pred->ReplaceSucc(endBlock, callContBb);
1500         }
1501     }
1502 }
1503 
1504 /**
1505  * Move constants of the inlined graph to the current one if same constant doesn't already exist.
1506  * If constant exists just fix callee graph's dataflow to use existing constants.
1507  */
MoveConstants(Graph * graphInl)1508 void Inlining::MoveConstants(Graph *graphInl)
1509 {
1510     auto startBb = graphInl->GetStartBlock();
1511     for (ConstantInst *constant = graphInl->GetFirstConstInst(), *nextConstant = nullptr; constant != nullptr;
1512          constant = nextConstant) {
1513         nextConstant = constant->GetNextConst();
1514         startBb->EraseInst(constant);
1515         auto exisingConstant = GetGraph()->FindOrAddConstant(constant);
1516         if (exisingConstant != constant) {
1517             constant->ReplaceUsers(exisingConstant);
1518         }
1519     }
1520 
1521     // Move NullPtr instruction
1522     if (graphInl->HasNullPtrInst()) {
1523         startBb->EraseInst(graphInl->GetNullPtrInst());
1524         auto exisingNullptr = GetGraph()->GetOrCreateNullPtr();
1525         graphInl->GetNullPtrInst()->ReplaceUsers(exisingNullptr);
1526     }
1527     // Move LoadUndefined instruction
1528     if (graphInl->HasUndefinedInst()) {
1529         startBb->EraseInst(graphInl->GetUndefinedInst());
1530         auto exisingUndefined = GetGraph()->GetOrCreateUndefinedInst();
1531         graphInl->GetUndefinedInst()->ReplaceUsers(exisingUndefined);
1532     }
1533 }
1534 
ResolveTarget(CallInst * callInst,InlineContext * ctx)1535 bool Inlining::ResolveTarget(CallInst *callInst, InlineContext *ctx)
1536 {
1537     auto runtime = GetGraph()->GetRuntime();
1538     auto method = callInst->GetCallMethod();
1539     if (callInst->GetOpcode() == Opcode::CallStatic) {
1540         ctx->method = method;
1541         return true;
1542     }
1543 
1544     if (g_options.IsCompilerNoVirtualInlining()) {
1545         return false;
1546     }
1547 
1548     // If class or method are final we can resolve the method
1549     if (runtime->IsMethodFinal(method) || runtime->IsClassFinal(runtime->GetClass(method))) {
1550         ctx->method = method;
1551         return true;
1552     }
1553 
1554     auto objectInst = callInst->GetDataFlowInput(callInst->GetObjectIndex());
1555     auto typeInfo = objectInst->GetObjectTypeInfo();
1556     if (CanUseTypeInfo(typeInfo, method)) {
1557         auto receiver = typeInfo.GetClass();
1558         MethodPtr resolvedMethod;
1559         if (runtime->IsInterfaceMethod(method)) {
1560             resolvedMethod = runtime->ResolveInterfaceMethod(receiver, method);
1561         } else {
1562             resolvedMethod = runtime->ResolveVirtualMethod(receiver, method);
1563         }
1564         if (resolvedMethod != nullptr && (typeInfo.IsExact() || runtime->IsMethodFinal(resolvedMethod))) {
1565             ctx->method = resolvedMethod;
1566             return true;
1567         }
1568         if (typeInfo.IsExact()) {
1569             LOG_INLINING(WARNING) << "Runtime failed to resolve method";
1570             return false;
1571         }
1572     }
1573 
1574     if (ArchTraits<RUNTIME_ARCH>::SUPPORT_DEOPTIMIZATION && !g_options.IsCompilerNoChaInlining() &&
1575         !GetGraph()->IsAotMode()) {
1576         // Try resolve via CHA
1577         auto cha = GetCha();
1578         if (cha != nullptr && cha->IsSingleImplementation(method)) {
1579             auto klass = runtime->GetClass(method);
1580             ctx->method = runtime->ResolveVirtualMethod(klass, callInst->GetCallMethod());
1581             if (ctx->method == nullptr) {
1582                 return false;
1583             }
1584             ctx->chaDevirtualize = true;
1585             return true;
1586         }
1587     }
1588 
1589     return false;
1590 }
1591 
CanUseTypeInfo(ObjectTypeInfo typeInfo,RuntimeInterface::MethodPtr method)1592 bool Inlining::CanUseTypeInfo(ObjectTypeInfo typeInfo, RuntimeInterface::MethodPtr method)
1593 {
1594     auto runtime = GetGraph()->GetRuntime();
1595     if (!typeInfo || runtime->IsInterface(typeInfo.GetClass())) {
1596         return false;
1597     }
1598     return runtime->IsAssignableFrom(runtime->GetClass(method), typeInfo.GetClass());
1599 }
1600 
InsertChaGuard(CallInst * callInst)1601 void Inlining::InsertChaGuard(CallInst *callInst)
1602 {
1603     auto saveState = callInst->GetSaveState();
1604     auto checkDeopt = GetGraph()->CreateInstIsMustDeoptimize(DataType::BOOL, callInst->GetPc());
1605     auto deopt =
1606         GetGraph()->CreateInstDeoptimizeIf(callInst->GetPc(), checkDeopt, saveState, DeoptimizeType::INLINE_CHA);
1607     callInst->InsertBefore(deopt);
1608     deopt->InsertBefore(checkDeopt);
1609 }
1610 
SkipBlock(const BasicBlock * block) const1611 bool Inlining::SkipBlock(const BasicBlock *block) const
1612 {
1613     if (block == nullptr || block->IsEmpty()) {
1614         return true;
1615     }
1616     if (!g_options.IsCompilerInliningSkipThrowBlocks() || (GetGraph()->GetThrowCounter(block) > 0)) {
1617         return false;
1618     }
1619     return block->IsEndWithThrowOrDeoptimize();
1620 }
1621 }  // namespace ark::compiler
1622