• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "call_graph.h"
17 
18 #include <algorithm>
19 #include <iostream>
20 #include <queue>
21 #include <unordered_set>
22 #include <fstream>
23 
24 #include "option.h"
25 #include "string_utils.h"
26 #include "maple_phase_manager.h"
27 
28 //                   Call Graph Analysis
29 // This phase is a foundation phase of compilation. This phase build
30 // the call graph not only for this module also for the modules it
31 // depends on when this phase is running for IPA.
32 // The main procedure shows as following.
33 // A. Devirtual virtual call of private final static and none-static
34 //    variable. This step aims to reduce the callee set for each call
35 //    which can benefit IPA analysis.
36 // B. Build Call Graph.
37 //    i)  For IPA, it rebuild all the call graph of the modules this
38 //        module depends on. All necessary information is stored in mplt.
39 //    ii) Analysis each function in this module. For each call statement
40 //        create a CGNode, and collect potential callee functions to
41 //        generate Call Graph.
42 // C. Find All Root Node for the Call Graph.
43 // D. Construct SCC based on Tarjan Algorithm
44 // E. Set compilation order as the bottom-up order of callgraph. So callee
45 //    is always compiled before caller. This benefits those optimizations
46 //    need interprocedure information like escape analysis.
47 namespace maple {
GetCallTypeName() const48 const std::string CallInfo::GetCallTypeName() const
49 {
50     switch (cType) {
51         case kCallTypeCall:
52             return "c";
53         case kCallTypeVirtualCall:
54             return "v";
55         case kCallTypeSuperCall:
56             return "s";
57         case kCallTypeInterfaceCall:
58             return "i";
59         case kCallTypeIcall:
60             return "icall";
61         case kCallTypeIntrinsicCall:
62             return "intrinsiccall";
63         case kCallTypeXinitrinsicCall:
64             return "xintrinsiccall";
65         case kCallTypeIntrinsicCallWithType:
66             return "intrinsiccallwithtype";
67         case kCallTypeFakeThreadStartRun:
68             return "fakecallstartrun";
69         case kCallTypeCustomCall:
70             return "customcall";
71         case kCallTypePolymorphicCall:
72             return "polymorphiccall";
73         default:
74             CHECK_FATAL(false, "unsupport CALL type");
75             return "";
76     }
77 }
78 
GetCalleeName() const79 const std::string CallInfo::GetCalleeName() const
80 {
81     if ((cType >= kCallTypeCall) && (cType <= kCallTypeInterfaceCall)) {
82         MIRFunction &mirf = *mirFunc;
83         return mirf.GetName();
84     } else if (cType == kCallTypeIcall) {
85         return "IcallUnknown";
86     } else if ((cType >= kCallTypeIntrinsicCall) && (cType <= kCallTypeIntrinsicCallWithType)) {
87         return "IntrinsicCall";
88     } else if (cType == kCallTypeCustomCall) {
89         return "CustomCall";
90     } else if (cType == kCallTypePolymorphicCall) {
91         return "PolymorphicCall";
92     }
93     CHECK_FATAL(false, "should not be here");
94     return "";
95 }
96 
DumpDetail() const97 void CGNode::DumpDetail() const
98 {
99     LogInfo::MapleLogger() << "---CGNode  @" << this << ": " << mirFunc->GetName() << "\t";
100     if (Options::profileUse && mirFunc->GetFuncProfData()) {
101         LogInfo::MapleLogger() << " Ffreq " << GetFuncFrequency() << "\t";
102     }
103     if (HasOneCandidate() != nullptr) {
104         LogInfo::MapleLogger() << "@One Candidate\n";
105     } else {
106         LogInfo::MapleLogger() << std::endl;
107     }
108     if (HasSetVCallCandidates()) {
109         for (uint32 i = 0; i < vcallCands.size(); ++i) {
110             LogInfo::MapleLogger() << "   virtual call candidates: " << vcallCands[i]->GetName() << "\n";
111         }
112     }
113     for (auto &callSite : callees) {
114         for (auto &cgIt : *callSite.second) {
115             CallInfo *ci = callSite.first;
116             CGNode *node = cgIt;
117             MIRFunction *mf = node->GetMIRFunction();
118             if (mf != nullptr) {
119                 LogInfo::MapleLogger() << "\tcallee in module : " << mf->GetName() << "  ";
120             } else {
121                 LogInfo::MapleLogger() << "\tcallee external: " << ci->GetCalleeName();
122             }
123             if (Options::profileUse) {
124                 LogInfo::MapleLogger() << " callsite freq" << GetCallsiteFrequency(ci->GetCallStmt());
125             }
126             LogInfo::MapleLogger() << "\n";
127         }
128     }
129     // dump caller
130     for (auto const &callerNode : GetCaller()) {
131         CHECK_NULL_FATAL(callerNode.first);
132         CHECK_NULL_FATAL(callerNode.first->mirFunc);
133         LogInfo::MapleLogger() << "\tcaller : " << callerNode.first->mirFunc->GetName() << std::endl;
134     }
135 }
136 
Dump(std::ofstream & fout) const137 void CGNode::Dump(std::ofstream &fout) const
138 {
139     // if dumpall == 1, dump whole call graph
140     // else dump callgraph with function defined in same module
141     CHECK_NULL_FATAL(mirFunc);
142     constexpr size_t withoutRingNodeSize = 1;
143     if (callees.empty()) {
144         fout << "\"" << mirFunc->GetName() << "\";\n";
145         return;
146     }
147     for (auto &callSite : callees) {
148         for (auto &cgIt : *callSite.second) {
149             CallInfo *ci = callSite.first;
150             CGNode *node = cgIt;
151             if (node == nullptr) {
152                 continue;
153             }
154             MIRFunction *func = node->GetMIRFunction();
155             fout << "\"" << mirFunc->GetName() << "\" -> ";
156             if (func != nullptr) {
157                 if (node->GetSCCNode() != nullptr && node->GetSCCNode()->GetNodes().size() > withoutRingNodeSize) {
158                     fout << "\"" << func->GetName() << "\"[label=" << node->GetSCCNode()->GetID() << " color=red];\n";
159                 } else {
160                     fout << "\"" << func->GetName() << "\"[label=" << 0 << " color=blue];\n";
161                 }
162             } else {
163                 // unknown / external function with empty function body
164                 fout << "\"" << ci->GetCalleeName() << "\"[label=" << ci->GetCallTypeName() << " color=blue];\n";
165             }
166         }
167     }
168 }
169 
AddCallsite(CallInfo * ci,MapleSet<CGNode *,Comparator<CGNode>> * callee)170 void CGNode::AddCallsite(CallInfo *ci, MapleSet<CGNode *, Comparator<CGNode>> *callee)
171 {
172     (void)callees.insert(std::pair<CallInfo *, MapleSet<CGNode *, Comparator<CGNode>> *>(ci, callee));
173 }
174 
AddCallsite(CallInfo & ci,CGNode * node)175 void CGNode::AddCallsite(CallInfo &ci, CGNode *node)
176 {
177     CHECK_FATAL(ci.GetCallType() != kCallTypeInterfaceCall, "must be true");
178     CHECK_FATAL(ci.GetCallType() != kCallTypeVirtualCall, "must be true");
179     auto *cgVector = alloc->GetMemPool()->New<MapleSet<CGNode *, Comparator<CGNode>>>(alloc->Adapter());
180     if (node != nullptr) {
181         node->AddNumRefs();
182         cgVector->insert(node);
183     }
184     (void)callees.emplace(&ci, cgVector);
185 }
186 
RemoveCallsite(const CallInfo * ci,CGNode * node)187 void CGNode::RemoveCallsite(const CallInfo *ci, CGNode *node)
188 {
189     for (Callsite callSite : GetCallee()) {
190         if (callSite.first == ci) {
191             auto cgIt = callSite.second->find(node);
192             if (cgIt != callSite.second->end()) {
193                 callSite.second->erase(cgIt);
194                 return;
195             }
196             CHECK_FATAL(false, "node isn't in ci");
197         }
198     }
199 }
200 
IsCalleeOf(CGNode * func) const201 bool CGNode::IsCalleeOf(CGNode *func) const
202 {
203     return callers.find(func) != callers.end();
204 }
205 
GetCallsiteFrequency(const StmtNode * callstmt) const206 int64_t CGNode::GetCallsiteFrequency(const StmtNode *callstmt) const
207 {
208     GcovFuncInfo *funcInfo = mirFunc->GetFuncProfData();
209     if (funcInfo->stmtFreqs.count(callstmt->GetStmtID()) > 0) {
210         return funcInfo->stmtFreqs[callstmt->GetStmtID()];
211     }
212     DEBUG_ASSERT(0, "should not be here");
213     return -1;
214 }
215 
GetFuncFrequency() const216 int64_t CGNode::GetFuncFrequency() const
217 {
218     GcovFuncInfo *funcInfo = mirFunc->GetFuncProfData();
219     if (funcInfo) {
220         return funcInfo->GetFuncFrequency();
221     }
222     DEBUG_ASSERT(0, "should not be here");
223     return 0;
224 }
225 
ClearFunctionList()226 void CallGraph::ClearFunctionList()
227 {
228     for (auto iter = mirModule->GetFunctionList().begin(); iter != mirModule->GetFunctionList().end();) {
229         if (GlobalTables::GetFunctionTable().GetFunctionFromPuidx((*iter)->GetPuidx()) == nullptr) {
230             (*iter)->ReleaseCodeMemory();
231             (*iter)->ReleaseMemory();
232             iter = mirModule->GetFunctionList().erase(iter);
233         } else {
234             ++iter;
235         }
236     }
237 }
238 
DelNode(CGNode & node)239 void CallGraph::DelNode(CGNode &node)
240 {
241     if (node.GetMIRFunction() == nullptr) {
242         return;
243     }
244     for (auto &callSite : node.GetCallee()) {
245         for (auto &cgIt : *callSite.second) {
246             cgIt->DelCaller(&node);
247             node.DelCallee(callSite.first, cgIt);
248             if (!cgIt->HasCaller() && cgIt->GetMIRFunction()->IsStatic() && !cgIt->IsAddrTaken()) {
249                 DelNode(*cgIt);
250             }
251         }
252     }
253     MIRFunction *func = node.GetMIRFunction();
254     // Delete the method of class info
255     if (func->GetClassTyIdx() != 0u) {
256         MIRType *classType = GlobalTables::GetTypeTable().GetTypeTable().at(func->GetClassTyIdx());
257         auto *mirStructType = static_cast<MIRStructType *>(classType);
258         size_t j = 0;
259         for (; j < mirStructType->GetMethods().size(); ++j) {
260             if (mirStructType->GetMethodsElement(j).first == func->GetStIdx()) {
261                 mirStructType->GetMethods().erase(mirStructType->GetMethods().begin() + static_cast<ssize_t>(j));
262                 break;
263             }
264         }
265     }
266     GlobalTables::GetFunctionTable().SetFunctionItem(func->GetPuidx(), nullptr);
267     DEBUG_ASSERT(func->GetFuncSymbol() != nullptr, "nullptr check");
268     // func will be erased, so the coressponding symbol should be set as Deleted
269     func->GetFuncSymbol()->SetIsDeleted();
270     nodesMap.erase(func);
271     // Update Klass info as it has been built
272     if (klassh->GetKlassFromFunc(func) != nullptr) {
273         klassh->GetKlassFromFunc(func)->DelMethod(*func);
274     }
275 }
276 
CallGraph(MIRModule & m,MemPool & memPool,MemPool & templPool,KlassHierarchy & kh,const std::string & fn)277 CallGraph::CallGraph(MIRModule &m, MemPool &memPool, MemPool &templPool, KlassHierarchy &kh, const std::string &fn)
278     : AnalysisResult(&memPool),
279       mirModule(&m),
280       cgAlloc(&memPool),
281       tempAlloc(&templPool),
282       mirBuilder(cgAlloc.GetMemPool()->New<MIRBuilder>(&m)),
283       entryNode(nullptr),
284       rootNodes(cgAlloc.Adapter()),
285       fileName(fn, &memPool),
286       klassh(&kh),
287       nodesMap(cgAlloc.Adapter()),
288       sccTopologicalVec(cgAlloc.Adapter()),
289       localConstValueMap(tempAlloc.Adapter()),
290       icallToFix(tempAlloc.Adapter()),
291       addressTakenPuidxs(tempAlloc.Adapter()),
292       numOfNodes(0)
293 {
294 }
295 
GetCallType(Opcode op) const296 CallType CallGraph::GetCallType(Opcode op) const
297 {
298     CallType typeTemp = kCallTypeInvalid;
299     switch (op) {
300         case OP_call:
301         case OP_callassigned:
302             typeTemp = kCallTypeCall;
303             break;
304         case OP_virtualcall:
305         case OP_virtualcallassigned:
306             typeTemp = kCallTypeVirtualCall;
307             break;
308         case OP_superclasscall:
309         case OP_superclasscallassigned:
310             typeTemp = kCallTypeSuperCall;
311             break;
312         case OP_interfacecall:
313         case OP_interfacecallassigned:
314             typeTemp = kCallTypeInterfaceCall;
315             break;
316         case OP_icall:
317         case OP_icallassigned:
318             typeTemp = kCallTypeIcall;
319             break;
320         case OP_intrinsiccall:
321         case OP_intrinsiccallassigned:
322             typeTemp = kCallTypeIntrinsicCall;
323             break;
324         case OP_xintrinsiccall:
325         case OP_xintrinsiccallassigned:
326             typeTemp = kCallTypeXinitrinsicCall;
327             break;
328         case OP_intrinsiccallwithtype:
329         case OP_intrinsiccallwithtypeassigned:
330             typeTemp = kCallTypeIntrinsicCallWithType;
331             break;
332         case OP_customcall:
333         case OP_customcallassigned:
334             typeTemp = kCallTypeCustomCall;
335             break;
336         case OP_polymorphiccall:
337         case OP_polymorphiccallassigned:
338             typeTemp = kCallTypePolymorphicCall;
339             break;
340         default:
341             break;
342     }
343     return typeTemp;
344 }
345 
GetCGNode(MIRFunction * func) const346 CGNode *CallGraph::GetCGNode(MIRFunction *func) const
347 {
348     if (nodesMap.find(func) != nodesMap.end()) {
349         return nodesMap.at(func);
350     }
351     return nullptr;
352 }
353 
GetCGNode(PUIdx puIdx) const354 CGNode *CallGraph::GetCGNode(PUIdx puIdx) const
355 {
356     return GetCGNode(GlobalTables::GetFunctionTable().GetFunctionFromPuidx(puIdx));
357 }
358 
GetSCCNode(MIRFunction * func) const359 SCCNode<CGNode> *CallGraph::GetSCCNode(MIRFunction *func) const
360 {
361     CGNode *cgNode = GetCGNode(func);
362     return (cgNode != nullptr) ? cgNode->GetSCCNode() : nullptr;
363 }
364 
UpdateCaleeCandidate(PUIdx callerPuIdx,IcallNode * icall,std::set<PUIdx> & candidate)365 void CallGraph::UpdateCaleeCandidate(PUIdx callerPuIdx, IcallNode *icall, std::set<PUIdx> &candidate)
366 {
367     CGNode *caller = GetCGNode(callerPuIdx);
368     CHECK_FATAL(caller != nullptr, "caller is null");
369     for (auto &pair : caller->GetCallee()) {
370         auto *callsite = pair.first;
371         if (callsite->GetCallStmt() == icall) {
372             auto *calleeSet = pair.second;
373             calleeSet->clear();
374             std::for_each(candidate.begin(), candidate.end(), [this, &calleeSet](PUIdx idx) {
375                 CGNode *callee = GetCGNode(idx);
376                 calleeSet->insert(callee);
377             });
378             return;
379         }
380     }
381 }
382 
UpdateCaleeCandidate(PUIdx callerPuIdx,IcallNode * icall,PUIdx calleePuidx,CallNode * call)383 void CallGraph::UpdateCaleeCandidate(PUIdx callerPuIdx, IcallNode *icall, PUIdx calleePuidx, CallNode *call)
384 {
385     CGNode *caller = GetCGNode(callerPuIdx);
386     CHECK_FATAL(caller != nullptr, "caller is null");
387     for (auto &pair : caller->GetCallee()) {
388         auto *callsite = pair.first;
389         if (callsite->GetCallStmt() == icall) {
390             callsite->SetCallStmt(call);
391             auto *calleeSet = pair.second;
392             calleeSet->clear();
393             calleeSet->insert(GetCGNode(calleePuidx));
394         }
395     }
396 }
397 
IsRootNode(MIRFunction * func) const398 bool CallGraph::IsRootNode(MIRFunction *func) const
399 {
400     if (GetCGNode(func) != nullptr) {
401         return (!GetCGNode(func)->HasCaller());
402     } else {
403         return false;
404     }
405 }
406 
407 // if expr has addroffunc expr as its opnd, store all the addroffunc puidx into result
CollectAddroffuncFromExpr(const BaseNode * expr)408 void CallGraph::CollectAddroffuncFromExpr(const BaseNode *expr)
409 {
410     DEBUG_ASSERT(expr != nullptr, "nullptr check");
411     if (expr->GetOpCode() == OP_addroffunc) {
412         addressTakenPuidxs.insert(static_cast<const AddroffuncNode *>(expr)->GetPUIdx());
413         return;
414     }
415     for (size_t i = 0; i < expr->GetNumOpnds(); ++i) {
416         CollectAddroffuncFromExpr(expr->Opnd(i));
417     }
418 }
419 
CollectAddroffuncFromStmt(const StmtNode * stmt)420 void CallGraph::CollectAddroffuncFromStmt(const StmtNode *stmt)
421 {
422     for (size_t i = 0; i < stmt->NumOpnds(); ++i) {
423         CollectAddroffuncFromExpr(stmt->Opnd(i));
424     }
425 }
426 
CollectAddroffuncFromConst(MIRConst * mirConst)427 void CallGraph::CollectAddroffuncFromConst(MIRConst *mirConst)
428 {
429     if (mirConst->GetKind() == kConstAddrofFunc) {
430         addressTakenPuidxs.insert(static_cast<MIRAddroffuncConst *>(mirConst)->GetValue());
431     } else if (mirConst->GetKind() == kConstAggConst) {
432         auto &constVec = static_cast<MIRAggConst *>(mirConst)->GetConstVec();
433         for (auto &cst : constVec) {
434             CollectAddroffuncFromConst(cst);
435         }
436     }
437 }
438 
ReplaceIcallToCall(BlockNode & body,IcallNode * icall,PUIdx newPUIdx)439 CallNode *CallGraph::ReplaceIcallToCall(BlockNode &body, IcallNode *icall, PUIdx newPUIdx)
440 {
441     MapleVector<BaseNode *> opnds(icall->GetNopnd().begin() + 1, icall->GetNopnd().end(),
442                                   CurFunction()->GetCodeMPAllocator().Adapter());
443     CallNode *newCall = nullptr;
444     if (icall->GetOpCode() == OP_icall) {
445         newCall = mirBuilder->CreateStmtCall(newPUIdx, opnds, OP_call);
446     } else if (icall->GetOpCode() == OP_icallassigned) {
447         newCall =
448             mirBuilder->CreateStmtCallAssigned(newPUIdx, opnds, icall->GetCallReturnSymbol(mirBuilder->GetMirModule()),
449                                                OP_callassigned, icall->GetRetTyIdx());
450     } else {
451         CHECK_FATAL(false, "NYI");
452     }
453     body.ReplaceStmt1WithStmt2(icall, newCall);
454     newCall->SetSrcPos(icall->GetSrcPos());
455     if (debugFlag) {
456         icall->Dump(0);
457         newCall->Dump(0);
458         LogInfo::MapleLogger() << "replace icall successfully!\n";
459     }
460     return newCall;
461 }
462 
GetFuncTypeFromFuncAddr(const BaseNode * base)463 MIRType *CallGraph::GetFuncTypeFromFuncAddr(const BaseNode *base)
464 {
465     MIRType *funcType = nullptr;
466     switch (base->GetOpCode()) {
467         case OP_dread: {
468             auto *dread = static_cast<const DreadNode *>(base);
469             const MIRSymbol *st = CurFunction()->GetLocalOrGlobalSymbol(dread->GetStIdx());
470             DEBUG_ASSERT(st != nullptr, "st should not be nullptr");
471             funcType = st->GetType();
472             if (funcType->IsStructType()) {
473                 funcType = static_cast<MIRStructType *>(funcType)->GetFieldType(dread->GetFieldID());
474             }
475             break;
476         }
477         case OP_iread: {
478             auto *iread = static_cast<const IreadNode *>(base);
479             funcType = iread->GetType();
480             break;
481         }
482         case OP_select: {
483             auto *select = static_cast<const TernaryNode *>(base);
484             funcType = GetFuncTypeFromFuncAddr(select->Opnd(kSecondOpnd));
485             if (funcType == nullptr) {
486                 funcType = GetFuncTypeFromFuncAddr(select->Opnd(kThirdOpnd));
487             }
488             break;
489         }
490         case OP_addroffunc: {
491             auto *funcNode = static_cast<const AddroffuncNode *>(base);
492             auto *func = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(funcNode->GetPUIdx());
493             funcType = func->GetMIRFuncType();
494             break;
495         }
496         default: {
497             CHECK_FATAL(false, "NYI");
498             break;
499         }
500     }
501     CHECK_FATAL(funcType != nullptr, "Error");
502     return funcType;
503 }
504 
FindRootNodes()505 void CallGraph::FindRootNodes()
506 {
507     if (!rootNodes.empty()) {
508         CHECK_FATAL(false, "rootNodes has already been set");
509     }
510     for (auto const &it : nodesMap) {
511         CGNode *node = it.second;
512         if (!node->HasCaller()) {
513             rootNodes.push_back(node);
514         }
515     }
516 }
517 
RemoveFileStaticRootNodes()518 void CallGraph::RemoveFileStaticRootNodes()
519 {
520     std::vector<CGNode *> staticRoots;
521     std::copy_if(
522         rootNodes.begin(), rootNodes.end(), std::inserter(staticRoots, staticRoots.begin()), [](const CGNode *root) {
523             // root means no caller, we should also make sure that root is not be used in addroffunc
524             auto mirFunc = root->GetMIRFunction();
525             return root != nullptr &&
526                    mirFunc != nullptr &&  // remove before
527                                           // if static functions or inline but not extern modified functions are not
528                                           // used anymore, they can be removed safely.
529                    !root->IsAddrTaken() && (mirFunc->IsStatic() || (mirFunc->IsInline() && !mirFunc->IsExtern()));
530         });
531     for (auto *root : staticRoots) {
532         // DFS delete root and its callee that is static and have no caller after root is deleted
533         DelNode(*root);
534     }
535     ClearFunctionList();
536     // rebuild rootNodes
537     rootNodes.clear();
538     FindRootNodes();
539 }
540 
Dump() const541 void CallGraph::Dump() const
542 {
543     for (auto const &it : nodesMap) {
544         CGNode *node = it.second;
545         node->DumpDetail();
546     }
547 }
548 
549 // Sort CGNode within an SCC. Try best to arrange callee appears before
550 // its (direct) caller, so that caller can benefit from summary info.
551 // If we have iterative inter-procedure analysis, then would not bother
552 // do this.
CGNodeCompare(CGNode * left,CGNode * right)553 static bool CGNodeCompare(CGNode *left, CGNode *right)
554 {
555     // special case: left calls right and right calls left, then compare by id
556     if (left->IsCalleeOf(right) && right->IsCalleeOf(left)) {
557         return left->GetID() < right->GetID();
558     }
559     // left is right's direct callee, then make left appears first
560     if (left->IsCalleeOf(right)) {
561         return true;
562     } else if (right->IsCalleeOf(left)) {
563         return false;
564     }
565     return left->GetID() < right->GetID();
566 }
567 
568 // Set compilation order as the bottom-up order of callgraph. So callee
569 // is always compiled before caller. This benifits thoses optimizations
570 // need interprocedure information like escape analysis.
SetCompilationFunclist() const571 void CallGraph::SetCompilationFunclist() const
572 {
573     mirModule->GetFunctionList().clear();
574     const MapleVector<SCCNode<CGNode> *> &sccTopVec = GetSCCTopVec();
575     for (MapleVector<SCCNode<CGNode> *>::const_reverse_iterator it = sccTopVec.rbegin(); it != sccTopVec.rend(); ++it) {
576         SCCNode<CGNode> *sccNode = *it;
577         std::sort(sccNode->GetNodes().begin(), sccNode->GetNodes().end(), CGNodeCompare);
578         for (auto const kIt : sccNode->GetNodes()) {
579             CGNode *node = kIt;
580             MIRFunction *func = node->GetMIRFunction();
581             if ((func != nullptr && func->GetBody() != nullptr && !IsInIPA()) ||
582                 (func != nullptr && !func->IsNative())) {
583                 mirModule->GetFunctionList().push_back(func);
584             }
585         }
586     }
587 }
588 
AddCandsForCallNode(const KlassHierarchy & kh)589 void CGNode::AddCandsForCallNode(const KlassHierarchy &kh)
590 {
591     // already set vcall candidates information
592     if (HasSetVCallCandidates()) {
593         return;
594     }
595     CHECK_NULL_FATAL(mirFunc);
596     Klass *klass = kh.GetKlassFromFunc(mirFunc);
597     if (klass != nullptr) {
598         MapleVector<MIRFunction *> *vec = klass->GetCandidates(mirFunc->GetBaseFuncNameWithTypeStrIdx());
599         if (vec != nullptr) {
600             vcallCands = *vec;  // Vector copy
601         }
602     }
603 }
604 
HasOneCandidate() const605 MIRFunction *CGNode::HasOneCandidate() const
606 {
607     int count = 0;
608     MIRFunction *cand = nullptr;
609     if (!mirFunc->IsEmpty()) {
610         ++count;
611         cand = mirFunc;
612     }
613     // scan candidates
614     for (size_t i = 0; i < vcallCands.size(); ++i) {
615         if (vcallCands[i] == nullptr) {
616             CHECK_FATAL(false, "must not be nullptr");
617         }
618         if (!vcallCands[i]->IsEmpty()) {
619             ++count;
620             if (cand == nullptr) {
621                 cand = vcallCands[i];
622             }
623         }
624     }
625     return (count == 1) ? cand : nullptr;
626 }
627 
628 }  // namespace maple
629