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 "me_cfg.h"
17 #include <iostream>
18 #include <algorithm>
19 #include <string>
20 #include "bb.h"
21 #include "ssa_mir_nodes.h"
22 #include "me_irmap.h"
23 #include "mir_builder.h"
24 #include "mir_lower.h"
25 #include "maple_phase_manager.h"
26
27 namespace {
28 constexpr int kFuncNameLenLimit = 80;
29 }
30
31 namespace maple {
32 #define MATCH_STMT(stmt, kOpCode) \
33 do { \
34 while ((stmt) != nullptr && (stmt)->GetOpCode() == OP_comment) { \
35 (stmt) = (stmt)->GetNext(); \
36 } \
37 if ((stmt) == nullptr || (stmt)->GetOpCode() != (kOpCode)) { \
38 return false; \
39 } \
40 } while (0) // END define
41
FindExprUse(const BaseNode & expr,StIdx stIdx) const42 bool MeCFG::FindExprUse(const BaseNode &expr, StIdx stIdx) const
43 {
44 if (expr.GetOpCode() == OP_addrof || expr.GetOpCode() == OP_dread) {
45 const auto &addofNode = static_cast<const AddrofNode &>(expr);
46 return addofNode.GetStIdx() == stIdx;
47 } else if (expr.GetOpCode() == OP_iread) {
48 return FindExprUse(*expr.Opnd(0), stIdx);
49 } else {
50 for (size_t i = 0; i < expr.NumOpnds(); ++i) {
51 if (FindExprUse(*expr.Opnd(i), stIdx)) {
52 return true;
53 }
54 }
55 }
56 return false;
57 }
58
FindDef(const StmtNode & stmt,StIdx stIdx) const59 bool MeCFG::FindDef(const StmtNode &stmt, StIdx stIdx) const
60 {
61 if (stmt.GetOpCode() != OP_dassign && !kOpcodeInfo.IsCallAssigned(stmt.GetOpCode())) {
62 return false;
63 }
64 if (stmt.GetOpCode() == OP_dassign) {
65 const auto &dassStmt = static_cast<const DassignNode &>(stmt);
66 return dassStmt.GetStIdx() == stIdx;
67 }
68 const auto &cNode = static_cast<const CallNode &>(stmt);
69 const CallReturnVector &nRets = cNode.GetReturnVec();
70 if (!nRets.empty()) {
71 DEBUG_ASSERT(nRets.size() == 1, "Single Ret value for now.");
72 StIdx idx = nRets[0].first;
73 RegFieldPair regFieldPair = nRets[0].second;
74 if (!regFieldPair.IsReg()) {
75 return idx == stIdx;
76 }
77 }
78 return false;
79 }
80
IsStartTryBB(maple::BB & meBB) const81 bool MeCFG::IsStartTryBB(maple::BB &meBB) const
82 {
83 if (!meBB.GetAttributes(kBBAttrIsTry) || meBB.GetAttributes(kBBAttrIsTryEnd)) {
84 return false;
85 }
86 return (!meBB.GetStmtNodes().empty() && meBB.GetStmtNodes().front().GetOpCode() == OP_try);
87 }
88
89 // CFG Verify
90 // Check bb-vec and bb-list are strictly consistent.
Verify() const91 void MeCFG::Verify() const
92 {
93 // Check every bb in bb-list.
94 auto eIt = valid_end();
95 for (auto bIt = valid_begin(); bIt != eIt; ++bIt) {
96 if (bIt == common_entry() || bIt == common_exit()) {
97 continue;
98 }
99 auto *bb = *bIt;
100 DEBUG_ASSERT(bb->GetBBId() < bbVec.size(), "CFG Error!");
101 DEBUG_ASSERT(bbVec.at(bb->GetBBId()) == bb, "CFG Error!");
102 if (bb->IsEmpty()) {
103 continue;
104 }
105 DEBUG_ASSERT(bb->GetKind() != kBBUnknown, "runtime check error");
106 // verify succ[1] is goto bb
107 if (bb->GetKind() == kBBCondGoto) {
108 if (!bb->GetAttributes(kBBAttrIsTry) && !bb->GetAttributes(kBBAttrWontExit)) {
109 DEBUG_ASSERT(bb->GetStmtNodes().rbegin().base().d() != nullptr, "runtime check error");
110 DEBUG_ASSERT(bb->GetSucc().size() == kBBVectorInitialSize, "runtime check error");
111 }
112 DEBUG_ASSERT(bb->GetSucc(1)->GetBBLabel() ==
113 static_cast<CondGotoNode &>(bb->GetStmtNodes().back()).GetOffset(),
114 "runtime check error");
115 } else if (bb->GetKind() == kBBGoto) {
116 if (bb->GetStmtNodes().back().GetOpCode() == OP_throw) {
117 continue;
118 }
119 if (!bb->GetAttributes(kBBAttrIsTry) && !bb->GetAttributes(kBBAttrWontExit)) {
120 DEBUG_ASSERT(bb->GetStmtNodes().rbegin().base().d() != nullptr, "runtime check error");
121 DEBUG_ASSERT(bb->GetSucc().size() == 1, "runtime check error");
122 }
123 DEBUG_ASSERT(bb->GetSucc(0)->GetBBLabel() == static_cast<GotoNode &>(bb->GetStmtNodes().back()).GetOffset(),
124 "runtime check error");
125 }
126 }
127 }
128
129 // check that all the target labels in jump statements are defined
VerifyLabels() const130 void MeCFG::VerifyLabels() const
131 {
132 auto eIt = valid_end();
133 for (auto bIt = valid_begin(); bIt != eIt; ++bIt) {
134 BB *mirBB = *bIt;
135 auto &stmtNodes = mirBB->GetStmtNodes();
136 if (stmtNodes.rbegin().base().d() == nullptr) {
137 continue;
138 }
139 if (mirBB->GetKind() == kBBGoto) {
140 if (stmtNodes.back().GetOpCode() == OP_throw) {
141 continue;
142 }
143 DEBUG_ASSERT(GetLabelBBAt(static_cast<GotoNode &>(stmtNodes.back()).GetOffset())->GetBBLabel() ==
144 static_cast<GotoNode &>(stmtNodes.back()).GetOffset(),
145 "undefined label in goto");
146 } else if (mirBB->GetKind() == kBBCondGoto) {
147 DEBUG_ASSERT(GetLabelBBAt(static_cast<CondGotoNode &>(stmtNodes.back()).GetOffset())->GetBBLabel() ==
148 static_cast<CondGotoNode &>(stmtNodes.back()).GetOffset(),
149 "undefined label in conditional branch");
150 } else if (mirBB->GetKind() == kBBSwitch) {
151 auto &switchStmt = static_cast<SwitchNode &>(stmtNodes.back());
152 LabelIdx targetLabIdx = switchStmt.GetDefaultLabel();
153 [[maybe_unused]] BB *bb = GetLabelBBAt(targetLabIdx);
154 DEBUG_ASSERT(bb->GetBBLabel() == targetLabIdx, "undefined label in switch");
155 for (size_t j = 0; j < switchStmt.GetSwitchTable().size(); ++j) {
156 targetLabIdx = switchStmt.GetCasePair(j).second;
157 bb = GetLabelBBAt(targetLabIdx);
158 DEBUG_ASSERT(bb->GetBBLabel() == targetLabIdx, "undefined switch target label");
159 }
160 }
161 }
162 }
163
Dump() const164 void MeCFG::Dump() const
165 {
166 // BSF Dump the cfg
167 LogInfo::MapleLogger() << "####### CFG Dump: ";
168 DEBUG_ASSERT(NumBBs() != 0, "size to be allocated is 0");
169 auto *visitedMap = static_cast<bool *>(calloc(NumBBs(), sizeof(bool)));
170 if (visitedMap != nullptr) {
171 std::queue<BB *> qu;
172 qu.push(GetFirstBB());
173 while (!qu.empty()) {
174 BB *bb = qu.front();
175 qu.pop();
176 if (bb == nullptr) {
177 continue;
178 }
179 BBId id = bb->GetBBId();
180 if (visitedMap[static_cast<long>(id)]) {
181 continue;
182 }
183 LogInfo::MapleLogger() << id << " ";
184 visitedMap[static_cast<long>(id)] = true;
185 auto it = bb->GetSucc().begin();
186 while (it != bb->GetSucc().end()) {
187 BB *kidBB = *it;
188 if (!visitedMap[static_cast<long>(kidBB->GetBBId())]) {
189 qu.push(kidBB);
190 }
191 ++it;
192 }
193 }
194 LogInfo::MapleLogger() << '\n';
195 free(visitedMap);
196 }
197 }
198
199 // replace special char in FunctionName for output file
ReplaceFilename(std::string & fileName)200 static void ReplaceFilename(std::string &fileName)
201 {
202 for (char &c : fileName) {
203 if (c == ';' || c == '/' || c == '|') {
204 c = '_';
205 }
206 }
207 }
208
ConstructFileNameToDump(const std::string & prefix) const209 std::string MeCFG::ConstructFileNameToDump(const std::string &prefix) const
210 {
211 std::string fileName;
212 if (!prefix.empty()) {
213 fileName.append(prefix);
214 fileName.append("-");
215 }
216 // the func name length may exceed OS's file name limit, so truncate after 80 chars
217 if (func.GetName().size() <= kFuncNameLenLimit) {
218 fileName.append(func.GetName());
219 } else {
220 fileName.append(func.GetName().c_str(), kFuncNameLenLimit);
221 }
222 ReplaceFilename(fileName);
223 fileName.append(".dot");
224 return fileName;
225 }
226
NewBasicBlock()227 BB *MeCFG::NewBasicBlock()
228 {
229 BB *newBB = memPool->New<BB>(&mecfgAlloc, &func.GetVersAlloc(), BBId(nextBBId++));
230 bbVec.push_back(newBB);
231 return newBB;
232 }
233
234 // new a basic block and insert before position or after position
InsertNewBasicBlock(const BB & position,bool isInsertBefore)235 BB &MeCFG::InsertNewBasicBlock(const BB &position, bool isInsertBefore)
236 {
237 BB *newBB = memPool->New<BB>(&mecfgAlloc, &func.GetVersAlloc(), BBId(nextBBId++));
238
239 auto bIt = std::find(begin(), end(), &position);
240 auto idx = position.GetBBId();
241 if (!isInsertBefore) {
242 ++bIt;
243 ++idx;
244 }
245 auto newIt = bbVec.insert(bIt, newBB);
246 auto eIt = end();
247 // update bb's idx
248 for (auto it = newIt; it != eIt; ++it) {
249 if ((*it) != nullptr) {
250 (*it)->SetBBId(BBId(idx));
251 }
252 ++idx;
253 }
254 return *newBB;
255 }
256
DeleteBasicBlock(const BB & bb)257 void MeCFG::DeleteBasicBlock(const BB &bb)
258 {
259 DEBUG_ASSERT(bbVec[bb.GetBBId()] == &bb, "runtime check error");
260 /* update first_bb_ and last_bb if needed */
261 bbVec.at(bb.GetBBId()) = nullptr;
262 }
263
264 /* get next bb in bbVec */
NextBB(const BB * bb)265 BB *MeCFG::NextBB(const BB *bb)
266 {
267 auto bbIt = std::find(begin(), end(), bb);
268 CHECK_FATAL(bbIt != end(), "bb must be inside bb_vec");
269 for (auto it = ++bbIt; it != end(); ++it) {
270 if (*it != nullptr) {
271 return *it;
272 }
273 }
274 return nullptr;
275 }
276
277 /* get prev bb in bbVec */
PrevBB(const BB * bb)278 BB *MeCFG::PrevBB(const BB *bb)
279 {
280 auto bbIt = std::find(rbegin(), rend(), bb);
281 CHECK_FATAL(bbIt != rend(), "bb must be inside bb_vec");
282 for (auto it = ++bbIt; it != rend(); ++it) {
283 if (*it != nullptr) {
284 return *it;
285 }
286 }
287 return nullptr;
288 }
289
SetTryBlockInfo(const StmtNode * nextStmt,StmtNode * tryStmt,BB * lastTryBB,BB * curBB,BB * newBB)290 void MeCFG::SetTryBlockInfo(const StmtNode *nextStmt, StmtNode *tryStmt, BB *lastTryBB, BB *curBB, BB *newBB)
291 {
292 if (nextStmt->GetOpCode() == OP_endtry) {
293 curBB->SetAttributes(kBBAttrIsTryEnd);
294 DEBUG_ASSERT(lastTryBB != nullptr, "null ptr check");
295 endTryBB2TryBB[curBB] = lastTryBB;
296 } else {
297 newBB->SetAttributes(kBBAttrIsTry);
298 bbTryNodeMap[newBB] = tryStmt;
299 }
300 }
301
BuildSCCDFS(BB & bb,uint32 & visitIndex,std::vector<SCCOfBBs * > & sccNodes,std::vector<uint32> & visitedOrder,std::vector<uint32> & lowestOrder,std::vector<bool> & inStack,std::stack<uint32> & visitStack)302 void MeCFG::BuildSCCDFS(BB &bb, uint32 &visitIndex, std::vector<SCCOfBBs *> &sccNodes,
303 std::vector<uint32> &visitedOrder, std::vector<uint32> &lowestOrder, std::vector<bool> &inStack,
304 std::stack<uint32> &visitStack)
305 {
306 uint32 id = bb.UintID();
307 visitedOrder[id] = visitIndex;
308 lowestOrder[id] = visitIndex;
309 ++visitIndex;
310 visitStack.push(id);
311 inStack[id] = true;
312
313 for (BB *succ : bb.GetSucc()) {
314 if (succ == nullptr) {
315 continue;
316 }
317 uint32 succId = succ->UintID();
318 if (!visitedOrder[succId]) {
319 BuildSCCDFS(*succ, visitIndex, sccNodes, visitedOrder, lowestOrder, inStack, visitStack);
320 if (lowestOrder[succId] < lowestOrder[id]) {
321 lowestOrder[id] = lowestOrder[succId];
322 }
323 } else if (inStack[succId]) {
324 (void)backEdges.emplace(std::pair<uint32, uint32>(id, succId));
325 if (visitedOrder[succId] < lowestOrder[id]) {
326 lowestOrder[id] = visitedOrder[succId];
327 }
328 }
329 }
330
331 if (visitedOrder.at(id) == lowestOrder.at(id)) {
332 auto *sccNode = GetAlloc().GetMemPool()->New<SCCOfBBs>(numOfSCCs++, &bb, &GetAlloc());
333 uint32 stackTopId;
334 do {
335 stackTopId = visitStack.top();
336 visitStack.pop();
337 inStack[stackTopId] = false;
338 auto *topBB = static_cast<BB *>(GetAllBBs()[stackTopId]);
339 sccNode->AddBBNode(topBB);
340 sccOfBB[stackTopId] = sccNode;
341 } while (stackTopId != id);
342
343 sccNodes.push_back(sccNode);
344 }
345 }
346
VerifySCC()347 void MeCFG::VerifySCC()
348 {
349 for (BB *bb : GetAllBBs()) {
350 if (bb == nullptr || bb == GetCommonExitBB()) {
351 continue;
352 }
353 SCCOfBBs *scc = sccOfBB.at(bb->UintID());
354 CHECK_FATAL(scc != nullptr, "bb should belong to a scc");
355 }
356 }
357
SCCTopologicalSort(std::vector<SCCOfBBs * > & sccNodes)358 void MeCFG::SCCTopologicalSort(std::vector<SCCOfBBs *> &sccNodes)
359 {
360 std::set<SCCOfBBs *> inQueue;
361 for (SCCOfBBs *node : sccNodes) {
362 if (!node->HasPred()) {
363 sccTopologicalVec.push_back(node);
364 (void)inQueue.insert(node);
365 }
366 }
367
368 // Top-down iterates all nodes
369 for (size_t i = 0; i < sccTopologicalVec.size(); ++i) {
370 SCCOfBBs *sccBB = sccTopologicalVec[i];
371 for (SCCOfBBs *succ : sccBB->GetSucc()) {
372 if (inQueue.find(succ) == inQueue.end()) {
373 // successor has not been visited
374 bool predAllVisited = true;
375 // check whether all predecessors of the current successor have been visited
376 for (SCCOfBBs *pred : succ->GetPred()) {
377 if (inQueue.find(pred) == inQueue.end()) {
378 predAllVisited = false;
379 break;
380 }
381 }
382 if (predAllVisited) {
383 sccTopologicalVec.push_back(succ);
384 (void)inQueue.insert(succ);
385 }
386 }
387 }
388 }
389 }
390
391 // BBId is index of BB in the bbVec, so we should swap two BB's pos in bbVec, if we want to swap their BBId.
SwapBBId(BB & bb1,BB & bb2)392 void MeCFG::SwapBBId(BB &bb1, BB &bb2)
393 {
394 bbVec[bb1.GetBBId()] = &bb2;
395 bbVec[bb2.GetBBId()] = &bb1;
396 BBId tmp = bb1.GetBBId();
397 bb1.SetBBId(bb2.GetBBId());
398 bb2.SetBBId(tmp);
399 }
400
401 // set bb succ frequency from bb freq
402 // no critical edge is expected
ConstructEdgeFreqFromBBFreq()403 void MeCFG::ConstructEdgeFreqFromBBFreq()
404 {
405 // set succfreqs
406 auto eIt = valid_end();
407 for (auto bIt = valid_begin(); bIt != eIt; ++bIt) {
408 auto *bb = *bIt;
409 if (!bb)
410 continue;
411 if (bb->GetSucc().size() == 1) {
412 bb->PushBackSuccFreq(bb->GetFrequency());
413 } else if (bb->GetSucc().size() == 2) { // bb has 2 succeed
414 auto *fallthru = bb->GetSucc(0);
415 auto *targetBB = bb->GetSucc(1);
416 if (fallthru->GetPred().size() == 1) {
417 auto succ0Freq = fallthru->GetFrequency();
418 bb->PushBackSuccFreq(succ0Freq);
419 DEBUG_ASSERT(bb->GetFrequency() >= succ0Freq, "sanity check");
420 bb->PushBackSuccFreq(bb->GetFrequency() - succ0Freq);
421 } else if (targetBB->GetPred().size() == 1) {
422 auto succ1Freq = targetBB->GetFrequency();
423 DEBUG_ASSERT(bb->GetFrequency() >= succ1Freq, "sanity check");
424 bb->PushBackSuccFreq(bb->GetFrequency() - succ1Freq);
425 bb->PushBackSuccFreq(succ1Freq);
426 } else {
427 CHECK_FATAL(false, "ConstructEdgeFreqFromBBFreq::NYI critical edge");
428 }
429 } else if (bb->GetSucc().size() > 2) { // bb has 2 succeed
430 // switch case, no critical edge is supposted
431 for (size_t i = 0; i < bb->GetSucc().size(); ++i) {
432 bb->PushBackSuccFreq(bb->GetSucc(i)->GetFrequency());
433 }
434 }
435 }
436 }
437
438 // set bb frequency from stmt record
ConstructBBFreqFromStmtFreq()439 void MeCFG::ConstructBBFreqFromStmtFreq()
440 {
441 GcovFuncInfo *funcData = func.GetMirFunc()->GetFuncProfData();
442 if (!funcData)
443 return;
444 if (funcData->stmtFreqs.size() == 0)
445 return;
446 auto eIt = valid_end();
447 for (auto bIt = valid_begin(); bIt != eIt; ++bIt) {
448 if ((*bIt)->IsEmpty())
449 continue;
450 StmtNode &first = (*bIt)->GetFirst();
451 if (funcData->stmtFreqs.count(first.GetStmtID()) > 0) {
452 (*bIt)->SetFrequency(funcData->stmtFreqs[first.GetStmtID()]);
453 } else if (funcData->stmtFreqs.count((*bIt)->GetLast().GetStmtID()) > 0) {
454 (*bIt)->SetFrequency(funcData->stmtFreqs[(*bIt)->GetLast().GetStmtID()]);
455 } else {
456 LogInfo::MapleLogger() << "ERROR:: bb " << (*bIt)->GetBBId() << "frequency is not set"
457 << "\n";
458 DEBUG_ASSERT(0, "no freq set");
459 }
460 }
461 // add common entry and common exit
462 auto *bb = *common_entry();
463 uint64_t freq = 0;
464 for (size_t i = 0; i < bb->GetSucc().size(); ++i) {
465 freq += bb->GetSucc(i)->GetFrequency();
466 }
467 bb->SetFrequency(freq);
468 bb = *common_exit();
469 freq = 0;
470 for (size_t i = 0; i < bb->GetPred().size(); ++i) {
471 freq += bb->GetPred(i)->GetFrequency();
472 }
473 bb->SetFrequency(freq);
474 // set succfreqs
475 ConstructEdgeFreqFromBBFreq();
476 // clear stmtFreqs since cfg frequency is create
477 funcData->stmtFreqs.clear();
478 }
479
ConstructStmtFreq()480 void MeCFG::ConstructStmtFreq()
481 {
482 GcovFuncInfo *funcData = func.GetMirFunc()->GetFuncProfData();
483 if (!funcData)
484 return;
485 auto eIt = valid_end();
486 // clear stmtFreqs
487 funcData->stmtFreqs.clear();
488 for (auto bIt = valid_begin(); bIt != eIt; ++bIt) {
489 auto *bb = *bIt;
490 if (bIt == common_entry()) {
491 funcData->entry_freq = bb->GetFrequency();
492 funcData->real_entryfreq = funcData->entry_freq;
493 }
494 for (auto &stmt : bb->GetStmtNodes()) {
495 Opcode op = stmt.GetOpCode();
496 // record bb start/end stmt
497 if (stmt.GetStmtID() == bb->GetFirst().GetStmtID() || stmt.GetStmtID() == bb->GetLast().GetStmtID() ||
498 IsCallAssigned(op) || op == OP_call) {
499 funcData->stmtFreqs[stmt.GetStmtID()] = bb->GetFrequency();
500 }
501 }
502 }
503 }
504
VerifyBBFreq()505 void MeCFG::VerifyBBFreq()
506 {
507 for (size_t i = 2; i < bbVec.size(); ++i) { // skip common entry and common exit
508 auto *bb = bbVec[i];
509 if (bb == nullptr || bb->GetAttributes(kBBAttrIsEntry) || bb->GetAttributes(kBBAttrIsExit)) {
510 continue;
511 }
512 // wontexit bb may has wrong succ, skip it
513 if (bb->GetSuccFreq().size() != bb->GetSucc().size() && !bb->GetAttributes(kBBAttrWontExit)) {
514 CHECK_FATAL(false, "VerifyBBFreq: succFreq size != succ size");
515 }
516 // bb freq == sum(out edge freq)
517 uint64 succSumFreq = 0;
518 for (auto succFreq : bb->GetSuccFreq()) {
519 succSumFreq += succFreq;
520 }
521 if (succSumFreq != bb->GetFrequency()) {
522 LogInfo::MapleLogger() << "[VerifyFreq failure] BB" << bb->GetBBId() << " freq: " << bb->GetFrequency()
523 << ", all succ edge freq sum: " << succSumFreq << std::endl;
524 LogInfo::MapleLogger() << func.GetName() << std::endl;
525 CHECK_FATAL(false, "VerifyFreq failure: bb freq != succ freq sum");
526 }
527 }
528 }
529 } // namespace maple
530