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