1 // -*- mode: C++; c-file-style: "cc-mode" -*-
2 //*************************************************************************
3 // DESCRIPTION: Verilator: Graph optimizations
4 //
5 // Code available from: https://verilator.org
6 //
7 //*************************************************************************
8 //
9 // Copyright 2003-2021 by Wilson Snyder. This program is free software; you
10 // can redistribute it and/or modify it under the terms of either the GNU
11 // Lesser General Public License Version 3 or the Perl Artistic License
12 // Version 2.0.
13 // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
14 //
15 //*************************************************************************
16 
17 #include "config_build.h"
18 #include "verilatedos.h"
19 
20 #include "V3Global.h"
21 #include "V3File.h"
22 #include "V3Graph.h"
23 
24 #include <map>
25 #include <memory>
26 #include <vector>
27 
28 int V3Graph::s_debug = 0;
debug()29 int V3Graph::debug() { return std::max(V3Error::debugDefault(), s_debug); }
30 
31 //######################################################################
32 //######################################################################
33 // Vertices
34 
V3GraphVertex(V3Graph * graphp,const V3GraphVertex & old)35 V3GraphVertex::V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old)
36     : m_fanout{old.m_fanout}
37     , m_color{old.m_color}
38     , m_rank{old.m_rank} {
39     m_userp = nullptr;
40     verticesPushBack(graphp);
41 }
42 
V3GraphVertex(V3Graph * graphp)43 V3GraphVertex::V3GraphVertex(V3Graph* graphp)
44     : m_fanout{0}
45     , m_color{0}
46     , m_rank{0} {
47     m_userp = nullptr;
48     verticesPushBack(graphp);
49 }
50 
verticesPushBack(V3Graph * graphp)51 void V3GraphVertex::verticesPushBack(V3Graph* graphp) {
52     m_vertices.pushBack(graphp->m_vertices, this);
53 }
54 
unlinkEdges(V3Graph *)55 void V3GraphVertex::unlinkEdges(V3Graph*) {
56     for (V3GraphEdge* edgep = outBeginp(); edgep; /*BELOW*/) {
57         V3GraphEdge* nextp = edgep->outNextp();
58         edgep->unlinkDelete();
59         edgep = nextp;
60     }
61     for (V3GraphEdge* edgep = inBeginp(); edgep; /*BELOW*/) {
62         V3GraphEdge* nextp = edgep->inNextp();
63         edgep->unlinkDelete();
64         edgep = nextp;
65     }
66 }
67 
unlinkDelete(V3Graph * graphp)68 void V3GraphVertex::unlinkDelete(V3Graph* graphp) {
69     // Delete edges
70     unlinkEdges(graphp);
71     // Unlink from vertex list
72     m_vertices.unlink(graphp->m_vertices, this);
73     // Delete
74     delete this;  // this=nullptr;
75 }
76 
rerouteEdges(V3Graph * graphp)77 void V3GraphVertex::rerouteEdges(V3Graph* graphp) {
78     // Make new edges for each from/to pair
79     for (V3GraphEdge* iedgep = inBeginp(); iedgep; iedgep = iedgep->inNextp()) {
80         for (V3GraphEdge* oedgep = outBeginp(); oedgep; oedgep = oedgep->outNextp()) {
81             new V3GraphEdge(graphp, iedgep->fromp(), oedgep->top(),
82                             std::min(iedgep->weight(), oedgep->weight()),
83                             iedgep->cutable() && oedgep->cutable());
84         }
85     }
86     // Remove old edges
87     unlinkEdges(graphp);
88 }
89 
inSize1() const90 bool V3GraphVertex::inSize1() const { return !inEmpty() && !inBeginp()->inNextp(); }
outSize1() const91 bool V3GraphVertex::outSize1() const { return !outEmpty() && !outBeginp()->outNextp(); }
92 
inHash() const93 uint32_t V3GraphVertex::inHash() const {
94     // We want the same hash ignoring the order of edges.
95     // So we need an associative operator, like XOR.
96     // However with XOR multiple edges to the same source will cancel out,
97     // so we use ADD.  (Generally call this only after removing duplicates though)
98     uint32_t hash = 0;
99     for (V3GraphEdge* edgep = this->inBeginp(); edgep; edgep = edgep->inNextp()) {
100         hash += cvtToHash(edgep->fromp());
101     }
102     return hash;
103 }
104 
outHash() const105 uint32_t V3GraphVertex::outHash() const {
106     uint32_t hash = 0;
107     for (V3GraphEdge* edgep = this->outBeginp(); edgep; edgep = edgep->outNextp()) {
108         hash += cvtToHash(edgep->top());
109     }
110     return hash;
111 }
112 
findConnectingEdgep(GraphWay way,const V3GraphVertex * waywardp)113 V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way, const V3GraphVertex* waywardp) {
114     // O(edges) linear search. Searches search both nodes' edge lists in
115     // parallel.  The lists probably aren't _both_ huge, so this is
116     // unlikely to blow up even on fairly nasty graphs.
117     const GraphWay inv = way.invert();
118     V3GraphEdge* aedgep = this->beginp(way);
119     V3GraphEdge* bedgep = waywardp->beginp(inv);
120     while (aedgep && bedgep) {
121         if (aedgep->furtherp(way) == waywardp) return aedgep;
122         if (bedgep->furtherp(inv) == this) return bedgep;
123         aedgep = aedgep->nextp(way);
124         bedgep = bedgep->nextp(inv);
125     }
126     return nullptr;
127 }
128 
v3errorEnd(std::ostringstream & str) const129 void V3GraphVertex::v3errorEnd(std::ostringstream& str) const {
130     std::ostringstream nsstr;
131     nsstr << str.str();
132     if (debug()) {
133         nsstr << endl;
134         nsstr << "-vertex: " << this << endl;
135     }
136     if (FileLine* const flp = fileline()) {
137         flp->v3errorEnd(nsstr);
138     } else {
139         V3Error::v3errorEnd(nsstr);
140     }
141 }
v3errorEndFatal(std::ostringstream & str) const142 void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const {
143     v3errorEnd(str);
144     assert(0);  // LCOV_EXCL_LINE
145     VL_UNREACHABLE
146 }
147 
operator <<(std::ostream & os,V3GraphVertex * vertexp)148 std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp) {
149     os << "  VERTEX=" << vertexp->name();
150     if (vertexp->rank()) os << " r" << vertexp->rank();
151     if (vertexp->fanout() != 0.0) os << " f" << vertexp->fanout();
152     if (vertexp->color()) os << " c" << vertexp->color();
153     return os;
154 }
155 
156 //######################################################################
157 //######################################################################
158 // Edges
159 
init(V3Graph * graphp,V3GraphVertex * fromp,V3GraphVertex * top,int weight,bool cutable)160 void V3GraphEdge::init(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight,
161                        bool cutable) {
162     UASSERT(fromp, "Null from pointer");
163     UASSERT(top, "Null to pointer");
164     m_fromp = fromp;
165     m_top = top;
166     m_weight = weight;
167     m_cutable = cutable;
168     m_userp = nullptr;
169     // Link vertices to this edge
170     outPushBack();
171     inPushBack();
172 }
173 
relinkFromp(V3GraphVertex * newFromp)174 V3GraphEdge* V3GraphEdge::relinkFromp(V3GraphVertex* newFromp) {
175     V3GraphEdge* oldNxt = outNextp();
176     m_outs.unlink(m_fromp->m_outs, this);
177     m_fromp = newFromp;
178     outPushBack();
179     return oldNxt;
180 }
181 
unlinkDelete()182 void V3GraphEdge::unlinkDelete() {
183     // Unlink from side
184     m_outs.unlink(m_fromp->m_outs, this);
185     // Unlink to side
186     m_ins.unlink(m_top->m_ins, this);
187     // Delete
188     delete this;  // this=nullptr;
189 }
190 
outPushBack()191 void V3GraphEdge::outPushBack() {
192     // m_fromp->m_outsp.push_back(this);
193     m_outs.pushBack(m_fromp->m_outs, this);
194 }
195 
inPushBack()196 void V3GraphEdge::inPushBack() {
197     // m_top->m_insp.push_back(this);
198     m_ins.pushBack(m_top->m_ins, this);
199 }
200 
201 //######################################################################
202 //######################################################################
203 // Graph top level
204 // Constructors
205 
V3Graph()206 V3Graph::V3Graph() {
207     // Anything here is probably needed in clear() also
208     verticesUnlink();
209 }
210 
~V3Graph()211 V3Graph::~V3Graph() { clear(); }
212 
clear()213 void V3Graph::clear() {
214     // Empty it of all points, as if making a new object
215     // Delete the old edges
216     for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
217         for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; /*BELOW*/) {
218             V3GraphEdge* nextp = edgep->outNextp();
219             VL_DO_DANGLING(delete edgep, edgep);
220             edgep = nextp;
221         }
222         vertexp->outUnlink();
223     }
224     // Delete the old vertices
225     for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; /*BELOW*/) {
226         V3GraphVertex* nextp = vertexp->verticesNextp();
227         VL_DO_DANGLING(delete vertexp, vertexp);
228         vertexp = nextp;
229     }
230     verticesUnlink();
231 }
232 
userClearVertices()233 void V3Graph::userClearVertices() {
234     // Clear user() in all of tree
235     // We may use the userCnt trick in V3Ast later... (but gblCnt would be
236     // in V3Graph instead of static - which has the complication of finding
237     // the graph pointer given a vertex.)  For now we don't call this often, and
238     // the extra code on each read of user() would probably slow things
239     // down more than help.
240     for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
241         vertexp->user(0);
242         vertexp->userp(nullptr);  // Its a union, but might be different size than user()
243     }
244 }
245 
userClearEdges()246 void V3Graph::userClearEdges() {
247     // Clear user() in all of tree
248     for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
249         for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
250             edgep->user(0);
251             edgep->userp(nullptr);  // Its a union, but might be different size than user()
252         }
253     }
254 }
255 
clearColors()256 void V3Graph::clearColors() {
257     // Reset colors
258     for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
259         vertexp->m_color = 0;
260     }
261 }
262 
263 //======================================================================
264 // Dumping
265 
loopsMessageCb(V3GraphVertex * vertexp)266 void V3Graph::loopsMessageCb(V3GraphVertex* vertexp) {
267     vertexp->v3fatalSrc("Loops detected in graph: " << vertexp);
268 }
269 
loopsVertexCb(V3GraphVertex * vertexp)270 void V3Graph::loopsVertexCb(V3GraphVertex* vertexp) {
271     // Needed here as V3GraphVertex<< isn't defined until later in header
272     if (debug()) std::cerr << "-Info-Loop: " << cvtToHex(vertexp) << " " << vertexp << endl;
273 }
274 
dump(std::ostream & os)275 void V3Graph::dump(std::ostream& os) {
276     // This generates a file used by graphviz, https://www.graphviz.org
277     os << " Graph:\n";
278     // Print vertices
279     for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
280         os << "\tNode: " << vertexp->name();
281         if (vertexp->color()) os << "  color=" << vertexp->color();
282         os << '\n';
283         // Print edges
284         for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
285             dumpEdge(os, vertexp, edgep);
286         }
287         for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
288             dumpEdge(os, vertexp, edgep);
289         }
290     }
291 }
292 
dumpEdge(std::ostream & os,V3GraphVertex * vertexp,V3GraphEdge * edgep)293 void V3Graph::dumpEdge(std::ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep) {
294     if (edgep->weight() && (edgep->fromp() == vertexp || edgep->top() == vertexp)) {
295         os << "\t\t";
296         if (edgep->fromp() == vertexp) os << "-> " << edgep->top()->name();
297         if (edgep->top() == vertexp) os << "<- " << edgep->fromp()->name();
298         if (edgep->cutable()) os << "  [CUTABLE]";
299         os << '\n';
300     }
301 }
302 
dumpDotFilePrefixed(const string & nameComment,bool colorAsSubgraph) const303 void V3Graph::dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph) const {
304     if (v3Global.opt.dumpTree()) {
305         dumpDotFile(v3Global.debugFilename(nameComment) + ".dot", colorAsSubgraph);
306     }
307 }
308 
309 //! Variant of dumpDotFilePrefixed without --dump option check
dumpDotFilePrefixedAlways(const string & nameComment,bool colorAsSubgraph) const310 void V3Graph::dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph) const {
311     dumpDotFile(v3Global.debugFilename(nameComment) + ".dot", colorAsSubgraph);
312 }
313 
dumpDotFile(const string & filename,bool colorAsSubgraph) const314 void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
315     // This generates a file used by graphviz, https://www.graphviz.org
316     // "hardcoded" parameters:
317     const std::unique_ptr<std::ofstream> logp{V3File::new_ofstream(filename)};
318     if (logp->fail()) v3fatal("Can't write " << filename);
319 
320     // Header
321     *logp << "digraph v3graph {\n";
322     *logp << "\tgraph\t[label=\"" << filename << "\",\n";
323     *logp << "\t\t labelloc=t, labeljust=l,\n";
324     *logp << "\t\t //size=\"7.5,10\",\n";
325     *logp << "\t\t rankdir=" << dotRankDir() << "];\n";
326 
327     // List of all possible subgraphs
328     std::multimap<std::string, V3GraphVertex*> subgraphs;
329     for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
330         const string vertexSubgraph
331             = (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : "";
332         subgraphs.emplace(vertexSubgraph, vertexp);
333     }
334 
335     // We use a map here, as we don't want to corrupt anything (userp) in the graph,
336     // and we don't care if this is slow.
337     std::unordered_map<const V3GraphVertex*, int> numMap;
338 
339     // Print vertices
340     int n = 0;
341     string subgr;
342     for (auto it = subgraphs.cbegin(); it != subgraphs.cend(); ++it) {
343         const string vertexSubgraph = it->first;
344         const V3GraphVertex* vertexp = it->second;
345         numMap[vertexp] = n;
346         if (subgr != vertexSubgraph) {
347             if (subgr != "") *logp << "\t};\n";
348             subgr = vertexSubgraph;
349             if (subgr != "") *logp << "\tsubgraph cluster_" << subgr << " {\n";
350         }
351         if (subgr != "") *logp << "\t";
352         *logp << "\tn" << vertexp->dotName() << (n++) << "\t[fontsize=8 "
353               << "label=\"" << (vertexp->name() != "" ? vertexp->name() : "\\N");
354         if (vertexp->rank()) *logp << " r" << vertexp->rank();
355         if (vertexp->fanout() != 0.0) *logp << " f" << vertexp->fanout();
356         if (vertexp->color()) *logp << "\\n c" << vertexp->color();
357         *logp << "\"";
358         *logp << ", color=" << vertexp->dotColor();
359         if (vertexp->dotStyle() != "") *logp << ", style=" << vertexp->dotStyle();
360         if (vertexp->dotShape() != "") *logp << ", shape=" << vertexp->dotShape();
361         *logp << "];\n";
362     }
363     if (subgr != "") *logp << "\t};\n";
364 
365     // Print edges
366     for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
367         for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
368             if (edgep->weight()) {
369                 const int fromVnum = numMap[edgep->fromp()];
370                 const int toVnum = numMap[edgep->top()];
371                 *logp << "\tn" << edgep->fromp()->dotName() << fromVnum << " -> n"
372                       << edgep->top()->dotName() << toVnum
373                       << " ["
374                       //<<"fontsize=8 label=\""<<(edgep->name()!="" ? edgep->name() : "\\E")<<"\""
375                       << "fontsize=8 label=\""
376                       << (edgep->dotLabel() != "" ? edgep->dotLabel() : "") << "\""
377                       << " weight=" << edgep->weight() << " color=" << edgep->dotColor();
378                 if (edgep->dotStyle() != "") *logp << " style=" << edgep->dotStyle();
379                 // if (edgep->cutable()) *logp << ",constraint=false";  // to rank without
380                 // following edges
381                 *logp << "];\n";
382             }
383         }
384     }
385     // Vertex::m_user end, now unused
386 
387     // Trailer
388     *logp << "}\n";
389     logp->close();
390 
391     cout << "dot -Tpdf -o ~/a.pdf " << filename << endl;
392 }
393