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