P4C
The P4 Compiler
Loading...
Searching...
No Matches
callGraph.h
1/*
2Copyright 2013-present Barefoot Networks, Inc.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#ifndef FRONTENDS_P4_CALLGRAPH_H_
18#define FRONTENDS_P4_CALLGRAPH_H_
19
20#include <algorithm>
21#include <unordered_set>
22#include <vector>
23
24#include "ir/ir.h"
25#include "lib/exceptions.h"
26#include "lib/log.h"
27#include "lib/map.h" // IWYU pragma: keep
28#include "lib/null.h"
29#include "lib/ordered_map.h"
30#include "lib/ordered_set.h"
31
32namespace P4 {
33
34// I could not get Util::toString() to work properly
35cstring cgMakeString(cstring s);
36cstring cgMakeString(char c);
37cstring cgMakeString(const IR::Node *node);
38cstring cgMakeString(const IR::INode *node);
39
40template <class T>
41class CallGraph {
42 protected:
43 cstring name;
44 // Use an ordered map to make this deterministic
45 ordered_map<T, std::vector<T> *> out_edges; // map caller to list of callees
47
48 public:
49 ordered_set<T> nodes; // all nodes; do not modify this directly
50 typedef typename ordered_map<T, std::vector<T> *>::const_iterator const_iterator;
51
52 explicit CallGraph(std::string_view name) : name(name) {}
53
54 const cstring &getName() const { return name; }
55
56 // Graph construction.
57
58 // node that may call no-one
59 void add(T caller) {
60 if (nodes.find(caller) != nodes.end()) return;
61 LOG1(name << ": " << cgMakeString(caller));
62 out_edges[caller] = new std::vector<T>();
63 in_edges[caller] = new std::vector<T>();
64 nodes.emplace(caller);
65 }
66 void calls(T caller, T callee) {
67 LOG1(name << ": " << cgMakeString(callee) << " is called by " << cgMakeString(caller));
68 add(caller);
69 add(callee);
70 out_edges[caller]->push_back(callee);
71 in_edges[callee]->push_back(caller);
72 }
73 void remove(T node) {
74 auto n = nodes.find(node);
75 BUG_CHECK(n != nodes.end(), "%1%: Node not in graph", node);
76 nodes.erase(n);
77 auto in = in_edges.find(node);
78 if (in != in_edges.end()) {
79 // remove all edges pointing to this node
80 for (auto n : *in->second) {
81 auto out = out_edges[n];
82 CHECK_NULL(out);
83 auto oe = std::find(out->begin(), out->end(), node);
84 out->erase(oe);
85 }
86 in_edges.erase(in);
87 }
88 auto it = out_edges.find(node);
89 if (it != out_edges.end()) {
90 // Remove all edges that point from this node
91 for (auto n : *it->second) {
92 auto in = in_edges[n];
93 CHECK_NULL(in);
94 auto ie = std::find(in->begin(), in->end(), node);
95 in->erase(ie);
96 }
97 out_edges.erase(it);
98 }
99 }
100
101 // Graph querying
102
103 bool isCallee(T callee) const {
104 auto callees = ::get(in_edges, callee);
105 return callees != nullptr && !callees->empty();
106 }
107 bool isCaller(T caller) const {
108 auto edges = ::get(out_edges, caller);
109 if (edges == nullptr) return false;
110 return !edges->empty();
111 }
112 // Iterators over the out_edges
113 const_iterator begin() const { return out_edges.cbegin(); }
114 const_iterator end() const { return out_edges.cend(); }
115 std::vector<T> *getCallees(T caller) { return out_edges[caller]; }
116 std::vector<T> *getCallers(T callee) { return in_edges[callee]; }
117 // Callees are appended to 'toAppend'
118 void getCallees(T caller, std::set<T> &toAppend) {
119 if (isCaller(caller)) toAppend.insert(out_edges[caller]->begin(), out_edges[caller]->end());
120 }
121 size_t size() const { return nodes.size(); }
122 // out will contain all nodes reachable from start
123 void reachable(T start, std::set<T> &out) const {
124 std::set<T> work;
125 work.emplace(start);
126 while (!work.empty()) {
127 T node = *work.begin();
128 work.erase(node);
129 if (out.find(node) != out.end()) continue;
130 out.emplace(node);
131 auto edges = out_edges.find(node);
132 if (edges == out_edges.end()) continue;
133 for (auto c : *(edges->second)) work.emplace(c);
134 }
135 }
136 // remove all nodes not in 'to'
137 void restrict(const std::set<T> &to) {
138 std::vector<T> toRemove;
139 for (auto n : nodes)
140 if (to.find(n) == to.end()) toRemove.push_back(n);
141 for (auto n : toRemove) remove(n);
142 }
143
144 typedef std::unordered_set<T> Set;
145
146 // Compute for each node the set of dominators with the indicated start node.
147 // Node d dominates node n if all paths from the start to n go through d
148 // Result is deposited in 'dominators'.
149 // 'dominators' should be empty when calling this function.
150 void dominators(T start, std::map<T, Set> &dominators) {
151 // initialize
152 for (auto n : nodes) {
153 if (n == start)
154 dominators[n].emplace(start);
155 else
156 dominators[n].insert(nodes.begin(), nodes.end());
157 }
158
159 // There are faster but more complicated algorithms.
160 bool changes = true;
161 while (changes) {
162 changes = false;
163 for (auto node : nodes) {
164 auto vec = in_edges[node];
165 if (vec == nullptr) continue;
166 auto size = dominators[node].size();
167 for (auto c : *vec) insersectWith(dominators[node], dominators[c]);
168 dominators[node].emplace(node);
169 if (dominators[node].size() != size) changes = true;
170 }
171 }
172 }
173
174 class Loop {
175 public:
176 T entry;
177 std::set<T> body;
178 // multiple back-edges could go to the same loop head
179 std::set<T> back_edge_heads;
180
181 explicit Loop(T entry) : entry(entry) {}
182 };
183
184 // All natural loops in a call-graph.
185 struct Loops {
186 std::vector<Loop *> loops;
187
188 // Return loop index if 'node' is a loop entry point
189 // else return -1
190 int isLoopEntryPoint(T node) const {
191 for (size_t i = 0; i < loops.size(); i++) {
192 auto loop = loops.at(i);
193 if (loop->entry == node) return i;
194 }
195 return -1;
196 }
197 bool isInLoop(int loopIndex, T node) const {
198 if (loopIndex == -1) return false;
199 auto loop = loops.at(loopIndex);
200 return (loop->body.find(node) != loop->body.end());
201 }
202 };
203
204 Loops *compute_loops(T start) {
205 auto result = new Loops();
206 std::map<T, Set> dom;
207 dominators(start, dom);
208
209 std::map<T, Loop *> entryToLoop;
210
211 for (auto e : nodes) {
212 auto next = out_edges[e];
213 auto dome = dom[e];
214 for (auto n : *next) {
215 if (dome.find(n) != dome.end()) {
216 // n is a loop head
217 auto loop = get(entryToLoop, n);
218 if (loop == nullptr) {
219 loop = new Loop(n);
220 entryToLoop[n] = loop;
221 result->loops.push_back(loop);
222 }
223 loop->back_edge_heads.emplace(e);
224 // reverse DFS from e to n
225
226 std::set<T> work;
227 work.emplace(e);
228 while (!work.empty()) {
229 auto crt = *work.begin();
230 work.erase(crt);
231 loop->body.emplace(crt);
232 if (crt == n) continue;
233 for (auto i : *in_edges[crt])
234 if (loop->body.find(i) == loop->body.end()) work.emplace(i);
235 }
236 }
237 }
238 }
239 return result;
240 }
241
242 protected:
243 // intersect in place
244 static void insersectWith(Set &set, Set &with) {
245 std::vector<T> toRemove;
246 for (auto e : set)
247 if (with.find(e) == with.end()) toRemove.push_back(e);
248 for (auto e : toRemove) set.erase(e);
249 }
250
251 // Helper for computing strongly-connected components
252 // using Tarjan's algorithm.
253 struct sccInfo {
254 unsigned crtIndex;
255 std::vector<T> stack;
256 std::set<T> onStack;
257 std::map<T, unsigned> index;
258 std::map<T, unsigned> lowlink;
259
260 sccInfo() : crtIndex(0) {}
261 void push(T node) {
262 stack.push_back(node);
263 onStack.emplace(node);
264 }
265 bool isOnStack(T node) { return onStack.count(node) != 0; }
266 bool unknown(T node) { return index.count(node) == 0; }
267 void setLowLink(T node, unsigned value) {
268 lowlink[node] = value;
269 LOG1(node << ".lowlink = " << value << " = " << get(lowlink, node));
270 }
271 void setLowLink(T node, T successor) {
272 unsigned nlink = get(lowlink, node);
273 unsigned slink = get(lowlink, successor);
274 if (slink < nlink) setLowLink(node, slink);
275 }
276 T pop() {
277 T result = stack.back();
278 stack.pop_back();
279 onStack.erase(result);
280 return result;
281 }
282 };
283
284 // helper for scSort
285 bool strongConnect(T node, sccInfo &helper, std::vector<T> &out) {
286 bool loop = false;
287
288 LOG1("scc " << cgMakeString(node));
289 helper.index.emplace(node, helper.crtIndex);
290 helper.setLowLink(node, helper.crtIndex);
291 helper.crtIndex++;
292 helper.push(node);
293 auto oe = out_edges[node];
294 if (oe != nullptr) {
295 for (auto next : *oe) {
296 LOG1(cgMakeString(node) << " => " << cgMakeString(next));
297 if (helper.unknown(next)) {
298 bool l = strongConnect(next, helper, out);
299 loop = loop | l;
300 helper.setLowLink(node, next);
301 } else if (helper.isOnStack(next)) {
302 helper.setLowLink(node, next);
303 if (next == node)
304 // the check below does not find self-loops
305 loop = true;
306 }
307 }
308 }
309
310 if (get(helper.lowlink, node) == get(helper.index, node)) {
311 LOG1(cgMakeString(node) << " index=" << get(helper.index, node)
312 << " lowlink=" << get(helper.lowlink, node));
313 while (true) {
314 T sccMember = helper.pop();
315 LOG1("Scc order " << cgMakeString(sccMember) << "[" << cgMakeString(node) << "]");
316 out.push_back(sccMember);
317 if (sccMember == node) break;
318 loop = true;
319 }
320 }
321
322 return loop;
323 }
324
325 public:
326 // Sort that computes strongly-connected components - all nodes in
327 // a strongly-connected components will be consecutive in the
328 // sort. Returns true if the graph contains at least one
329 // cycle. Ignores nodes not reachable from 'start'.
330 bool sccSort(T start, std::vector<T> &out) {
331 sccInfo helper;
332 return strongConnect(start, helper, out);
333 }
334 bool sort(std::vector<T> &start, std::vector<T> &out) {
335 sccInfo helper;
336 bool cycles = false;
337 for (auto n : start) {
338 if (std::find(out.begin(), out.end(), n) == out.end()) {
339 bool c = strongConnect(n, helper, out);
340 cycles = cycles || c;
341 }
342 }
343 return cycles;
344 }
345 bool sort(std::vector<T> &out) {
346 sccInfo helper;
347 bool cycles = false;
348 for (auto n : nodes) {
349 if (std::find(out.begin(), out.end(), n) == out.end()) {
350 bool c = strongConnect(n, helper, out);
351 cycles = cycles || c;
352 }
353 }
354 return cycles;
355 }
356};
357
358} // namespace P4
359
360#endif /* FRONTENDS_P4_CALLGRAPH_H_ */
Definition node.h:64
Definition node.h:93
Definition callGraph.h:174
Definition callGraph.h:41
Definition cstring.h:80
Definition ordered_map.h:30
Definition ordered_set.h:30
Definition applyOptionsPragmas.cpp:24
Definition callGraph.h:185
Definition callGraph.h:253