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