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 #ifndef MPL2MPL_INCLUDE_SCC_H
17 #define MPL2MPL_INCLUDE_SCC_H
18 #include "base_graph_node.h"
19 namespace maple {
20 class BaseGraphNode;
21 
22 constexpr uint32 kShiftSccUniqueIDNum = 16;
23 
24 // Note that T is the type of the graph node.
25 template <typename T>
26 class SCCNode {
27 public:
SCCNode(uint32 index, MapleAllocator &alloc)28     SCCNode(uint32 index, MapleAllocator &alloc)
29         : id(index), nodes(alloc.Adapter()), inScc(alloc.Adapter()), outScc(alloc.Adapter())
30     {
31     }
32 
33     ~SCCNode() = default;
34 
AddNode(T *node)35     void AddNode(T *node)
36     {
37         nodes.push_back(node);
38     }
39 
Dump() const40     void Dump() const
41     {
42         LogInfo::MapleLogger() << "SCC " << id << " contains " << nodes.size() << " node(s)\n";
43         for (auto const kIt : nodes) {
44             T *node = kIt;
45             LogInfo::MapleLogger() << "  " << node->GetIdentity() << "\n";
46         }
47     }
48 
DumpCycle() const49     void DumpCycle() const
50     {
51         T *currNode = nodes[0];
52         std::vector<T *> searched;
53         searched.push_back(currNode);
54         std::vector<T *> invalidNodes;
55         std::vector<BaseGraphNode *> outNodes;
56         while (true) {
57             bool findNewOut = false;
58             currNode->GetOutNodes(outNodes);
59             for (auto outIt : outNodes) {
60                 auto outNode = static_cast<T *>(outIt);
61                 if (outNode->GetSCCNode() == this) {
62                     size_t j = 0;
63                     for (; j < invalidNodes.size(); ++j) {
64                         if (invalidNodes[j] == outNode) {
65                             break;
66                         }
67                     }
68                     // Find a invalid node
69                     if (j < invalidNodes.size()) {
70                         continue;
71                     }
72                     for (j = 0; j < searched.size(); ++j) {
73                         if (searched[j] == outNode) {
74                             break;
75                         }
76                     }
77                     if (j == searched.size()) {
78                         currNode = outNode;
79                         searched.push_back(currNode);
80                         findNewOut = true;
81                         break;
82                     }
83                 }
84             }
85             outNodes.clear();
86             if (searched.size() == nodes.size()) {
87                 break;
88             }
89             if (!findNewOut) {
90                 invalidNodes.push_back(searched[searched.size() - 1]);
91                 searched.pop_back();
92                 currNode = searched[searched.size() - 1];
93             }
94         }
95         for (auto it = searched.begin(); it != searched.end(); ++it) {
96             LogInfo::MapleLogger() << (*it)->GetIdentity() << '\n';
97         }
98     }
99 
Verify() const100     void Verify() const
101     {
102         CHECK_FATAL(!nodes.empty(), "the size of nodes less than zero");
103         for (T *const &node : nodes) {
104             if (node->GetSCCNode() != this) {
105                 CHECK_FATAL(false, "must equal this");
106             }
107         }
108     }
109 
Setup()110     void Setup()
111     {
112         std::vector<BaseGraphNode *> outNodes;
113         std::vector<BaseGraphNode *> inNodes;
114         for (T *const &node : nodes) {
115             node->GetOutNodes(outNodes);
116             for (auto outIt : outNodes) {
117                 auto outNode = static_cast<T *>(outIt);
118                 if (outNode == nullptr) {
119                     continue;
120                 }
121                 auto outNodeScc = outNode->GetSCCNode();
122                 if (outNodeScc == this) {
123                     continue;
124                 }
125                 outScc.insert(outNodeScc);
126                 outNodeScc->inScc.insert(this);
127             }
128             outNodes.clear();
129         }
130     }
131 
GetNodes() const132     const MapleVector<T *> &GetNodes() const
133     {
134         return nodes;
135     }
136 
GetNodes()137     MapleVector<T *> &GetNodes()
138     {
139         return nodes;
140     }
141 
GetOutScc() const142     const MapleSet<SCCNode<T> *, Comparator<SCCNode<T>>> &GetOutScc() const
143     {
144         return outScc;
145     }
146 
GetInScc() const147     const MapleSet<SCCNode<T> *, Comparator<SCCNode<T>>> &GetInScc() const
148     {
149         return inScc;
150     }
151 
RemoveInScc(SCCNode<T> *const sccNode)152     void RemoveInScc(SCCNode<T> *const sccNode)
153     {
154         inScc.erase(sccNode);
155     }
156 
HasRecursion() const157     bool HasRecursion() const
158     {
159         if (nodes.empty()) {
160             return false;
161         }
162         if (nodes.size() > 1) {
163             return true;
164         }
165         T *node = nodes[0];
166         std::vector<BaseGraphNode *> outNodes;
167         node->GetOutNodes(outNodes);
168         for (auto outIt : outNodes) {
169             auto outNode = static_cast<T *>(outIt);
170             if (outNode == nullptr) {
171                 continue;
172             }
173             if (node == outNode) {
174                 return true;
175             }
176         }
177         return false;
178     }
179 
HasSelfRecursion() const180     bool HasSelfRecursion() const
181     {
182         if (nodes.size() != 1) {
183             return false;
184         }
185         T *node = nodes[0];
186         std::vector<BaseGraphNode *> outNodes;
187         node->GetOutNodes(outNodes);
188         for (auto outIt : outNodes) {
189             auto outNode = static_cast<T *>(outIt);
190             if (outNode == nullptr) {
191                 continue;
192             }
193             if (node == outNode) {
194                 return true;
195             }
196         }
197         return false;
198     }
199 
HasInScc() const200     bool HasInScc() const
201     {
202         return (!inScc.empty());
203     }
204 
GetID() const205     uint32 GetID() const
206     {
207         return id;
208     }
209 
GetUniqueID() const210     uint32 GetUniqueID() const
211     {
212         return GetID() << maple::kShiftSccUniqueIDNum;
213     }
214 
215 private:
216     uint32 id;
217     MapleVector<T *> nodes;
218     MapleSet<SCCNode<T> *, Comparator<SCCNode<T>>> inScc;
219     MapleSet<SCCNode<T> *, Comparator<SCCNode<T>>> outScc;
220 };
221 
222 template <typename T>
BuildSCCDFS(T &rootNode, uint32 &visitIndex, MapleVector<SCCNode<T> *> &sccNodes, std::vector<T *> &nodes, std::vector<uint32> &visitedOrder, std::vector<uint32> &lowestOrder, std::vector<bool> &inStack, std::vector<uint32> &visitStack, uint32 &numOfSccs, MapleAllocator &cgAlloc)223 void BuildSCCDFS(T &rootNode, uint32 &visitIndex, MapleVector<SCCNode<T> *> &sccNodes, std::vector<T *> &nodes,
224                  std::vector<uint32> &visitedOrder, std::vector<uint32> &lowestOrder, std::vector<bool> &inStack,
225                  std::vector<uint32> &visitStack, uint32 &numOfSccs, MapleAllocator &cgAlloc)
226 {
227     uint32 id = rootNode.GetID();
228     nodes.at(id) = &rootNode;
229     visitedOrder.at(id) = visitIndex;
230     lowestOrder.at(id) = visitIndex;
231     ++visitIndex;
232     inStack.at(id) = true;
233 
234     std::vector<BaseGraphNode *> outNodes;
235     rootNode.GetOutNodes(outNodes);
236     for (auto outIt : outNodes) {
237         auto outNode = static_cast<T *>(outIt);
238         if (outNode == nullptr) {
239             continue;
240         }
241         uint32 outNodeId = outNode->GetID();
242         if (visitedOrder.at(outNodeId) == 0) {
243             // callee has not been processed yet
244             BuildSCCDFS(*outNode, visitIndex, sccNodes, nodes, visitedOrder, lowestOrder, inStack, visitStack,
245                         numOfSccs, cgAlloc);
246             if (lowestOrder.at(outNodeId) < lowestOrder.at(id)) {
247                 lowestOrder.at(id) = lowestOrder.at(outNodeId);
248             }
249         } else if (inStack.at(outNodeId) && (visitedOrder.at(outNodeId) < lowestOrder.at(id))) {
250             // back edge
251             lowestOrder.at(id) = visitedOrder.at(outNodeId);
252         }
253     }
254 
255     if (visitedOrder.at(id) == lowestOrder.at(id)) {
256         SCCNode<T> *sccNode = cgAlloc.GetMemPool()->New<SCCNode<T>>(numOfSccs++, cgAlloc);
257         inStack.at(id) = false;
258         T *rootNode = nodes.at(id);
259         rootNode->SetSCCNode(sccNode);
260         sccNode->AddNode(rootNode);
261         while (!visitStack.empty()) {
262             auto stackTopId = visitStack.back();
263             if (visitedOrder.at(stackTopId) < visitedOrder.at(id)) {
264                 break;
265             }
266             visitStack.pop_back();
267             inStack.at(stackTopId) = false;
268             T *topNode = nodes.at(stackTopId);
269             topNode->SetSCCNode(sccNode);
270             sccNode->AddNode(topNode);
271         }
272         sccNodes.push_back(sccNode);
273     } else {
274         visitStack.push_back(id);
275     }
276 }
277 
278 template <typename T>
VerifySCC(std::vector<T *> nodes)279 void VerifySCC(std::vector<T *> nodes)
280 {
281     for (auto node : nodes) {
282         if (node->GetSCCNode() == nullptr) {
283             CHECK_FATAL(false, "nullptr check in VerifySCC()");
284         }
285     }
286 }
287 
288 template <typename T>
BuildSCC(MapleAllocator &cgAlloc, uint32 numOfNodes, std::vector<T *> &allNodes, bool debugScc, MapleVector<SCCNode<T> *> &topologicalVec, bool clearOld = false)289 uint32 BuildSCC(MapleAllocator &cgAlloc, uint32 numOfNodes, std::vector<T *> &allNodes, bool debugScc,
290                 MapleVector<SCCNode<T> *> &topologicalVec, bool clearOld = false)
291 {
292     // This is the mapping between cg_id to node.
293     std::vector<T *> id2NodeMap(numOfNodes, nullptr);
294     std::vector<uint32> visitedOrder(numOfNodes, 0);
295     std::vector<uint32> lowestOrder(numOfNodes, 0);
296     std::vector<bool> inStack(numOfNodes, false);
297     std::vector<uint32> visitStack;
298     uint32 visitIndex = 1;
299     uint32 numOfSccs = 0;
300     if (clearOld) {
301         // clear old scc before computing
302         for (auto node : allNodes) {
303             node->SetSCCNode(nullptr);
304         }
305     }
306     // However, not all SCC can be reached from roots.
307     // E.g. foo()->foo(), foo is not considered as a root.
308     for (auto node : allNodes) {
309         if (node->GetSCCNode() == nullptr) {
310             BuildSCCDFS(*node, visitIndex, topologicalVec, id2NodeMap, visitedOrder, lowestOrder, inStack, visitStack,
311                         numOfSccs, cgAlloc);
312         }
313     }
314     for (auto scc : topologicalVec) {
315         scc->Verify();
316         scc->Setup();  // fix caller and callee info.
317         if (debugScc && scc->HasRecursion()) {
318             scc->Dump();
319         }
320     }
321     std::reverse(topologicalVec.begin(), topologicalVec.end());
322     return numOfSccs;
323 }
324 }  // namespace maple
325 #endif  // MPL2MPL_INCLUDE_SCC_H