cyclegraph: small cleanup
[cdsspec-compiler.git] / cyclegraph.cc
1 #include "cyclegraph.h"
2 #include "action.h"
3 #include "common.h"
4 #include "promise.h"
5 #include "model.h"
6
7 /** Initializes a CycleGraph object. */
8 CycleGraph::CycleGraph() :
9         discovered(new HashTable<CycleNode *, CycleNode *, uintptr_t, 4, model_malloc, model_calloc, model_free>(16)),
10         hasCycles(false),
11         oldCycles(false),
12         hasRMWViolation(false),
13         oldRMWViolation(false)
14 {
15 }
16
17 /** CycleGraph destructor */
18 CycleGraph::~CycleGraph()
19 {
20 }
21
22 /**
23  * @brief Returns the CycleNode corresponding to a given ModelAction
24  * @param action The ModelAction to find a node for
25  * @return The CycleNode paired with this action
26  */
27 CycleNode * CycleGraph::getNode(const ModelAction *action)
28 {
29         CycleNode *node = actionToNode.get(action);
30         if (node == NULL) {
31                 node = new CycleNode(action);
32                 actionToNode.put(action, node);
33 #if SUPPORT_MOD_ORDER_DUMP
34                 nodeList.push_back(node);
35 #endif
36         }
37         return node;
38 }
39
40 /**
41  * Adds an edge between two ModelActions. The ModelAction @a to is ordered
42  * after the ModelAction @a from.
43  * @param to The edge points to this ModelAction
44  * @param from The edge comes from this ModelAction
45  */
46 void CycleGraph::addEdge(const ModelAction *from, const ModelAction *to)
47 {
48         ASSERT(from);
49         ASSERT(to);
50
51         CycleNode *fromnode = getNode(from);
52         CycleNode *tonode = getNode(to);
53
54         if (!hasCycles) {
55                 // Reflexive edges are cycles
56                 hasCycles = (from == to);
57         }
58         if (!hasCycles) {
59                 // Check for Cycles
60                 hasCycles = checkReachable(tonode, fromnode);
61         }
62
63         if (fromnode->addEdge(tonode))
64                 rollbackvector.push_back(fromnode);
65
66
67         CycleNode *rmwnode = fromnode->getRMW();
68
69         /*
70          * If the fromnode has a rmwnode that is not the tonode, we should add
71          * an edge between its rmwnode and the tonode
72          *
73          * If tonode is also a rmw, don't do this check as the execution is
74          * doomed and we'll catch the problem elsewhere, but we want to allow
75          * for the possibility of sending to's write value to rmwnode
76          */
77         if (rmwnode != NULL && !to->is_rmw()) {
78                 if (!hasCycles) {
79                         // Check for Cycles
80                         hasCycles = checkReachable(tonode, rmwnode);
81                 }
82
83                 if (rmwnode->addEdge(tonode))
84                         rollbackvector.push_back(rmwnode);
85         }
86 }
87
88 /** Handles special case of a RMW action.  The ModelAction rmw reads
89  *  from the ModelAction from.  The key differences are: (1) no write
90  *  can occur in between the rmw and the from action.  Only one RMW
91  *  action can read from a given write.
92  */
93 void CycleGraph::addRMWEdge(const ModelAction *from, const ModelAction *rmw)
94 {
95         ASSERT(from);
96         ASSERT(rmw);
97
98         CycleNode *fromnode = getNode(from);
99         CycleNode *rmwnode = getNode(rmw);
100
101         /* Two RMW actions cannot read from the same write. */
102         if (fromnode->setRMW(rmwnode)) {
103                 hasRMWViolation = true;
104         } else {
105                 rmwrollbackvector.push_back(fromnode);
106         }
107
108         /* Transfer all outgoing edges from the from node to the rmw node */
109         /* This process should not add a cycle because either:
110          * (1) The rmw should not have any incoming edges yet if it is the
111          * new node or
112          * (2) the fromnode is the new node and therefore it should not
113          * have any outgoing edges.
114          */
115         for (unsigned int i = 0; i < fromnode->getNumEdges(); i++) {
116                 CycleNode *tonode = fromnode->getEdge(i);
117                 if (tonode != rmwnode) {
118                         if (rmwnode->addEdge(tonode))
119                                 rollbackvector.push_back(rmwnode);
120                 }
121         }
122
123
124         if (!hasCycles) {
125                 // Reflexive edges are cycles
126                 hasCycles = (from == rmw);
127         }
128         if (!hasCycles) {
129                 // With promises we could be setting up a cycle here if we aren't
130                 // careful...avoid it..
131                 hasCycles = checkReachable(rmwnode, fromnode);
132         }
133         if (fromnode->addEdge(rmwnode))
134                 rollbackvector.push_back(fromnode);
135 }
136
137 #if SUPPORT_MOD_ORDER_DUMP
138 void CycleGraph::dumpNodes(FILE *file) const
139 {
140         for (unsigned int i = 0; i < nodeList.size(); i++) {
141                 CycleNode *cn = nodeList[i];
142                 const ModelAction *action = cn->getAction();
143                 fprintf(file, "N%u [label=\"%u, T%u\"];\n", action->get_seq_number(), action->get_seq_number(), action->get_tid());
144                 if (cn->getRMW() != NULL) {
145                         fprintf(file, "N%u -> N%u[style=dotted];\n", action->get_seq_number(), cn->getRMW()->getAction()->get_seq_number());
146                 }
147                 for (unsigned int j = 0; j < cn->getNumEdges(); j++) {
148                         CycleNode *dst = cn->getEdge(j);
149                         const ModelAction *dstaction = dst->getAction();
150                         fprintf(file, "N%u -> N%u;\n", action->get_seq_number(), dstaction->get_seq_number());
151                 }
152         }
153 }
154
155 void CycleGraph::dumpGraphToFile(const char *filename) const
156 {
157         char buffer[200];
158         sprintf(buffer, "%s.dot", filename);
159         FILE *file = fopen(buffer, "w");
160         fprintf(file, "digraph %s {\n", filename);
161         dumpNodes(file);
162         fprintf(file, "}\n");
163         fclose(file);
164 }
165 #endif
166
167 /**
168  * Checks whether one ModelAction can reach another.
169  * @param from The ModelAction from which to begin exploration
170  * @param to The ModelAction to reach
171  * @return True, @a from can reach @a to; otherwise, false
172  */
173 bool CycleGraph::checkReachable(const ModelAction *from, const ModelAction *to) const
174 {
175         CycleNode *fromnode = actionToNode.get(from);
176         CycleNode *tonode = actionToNode.get(to);
177
178         if (!fromnode || !tonode)
179                 return false;
180
181         return checkReachable(fromnode, tonode);
182 }
183
184 /**
185  * Checks whether one CycleNode can reach another.
186  * @param from The CycleNode from which to begin exploration
187  * @param to The CycleNode to reach
188  * @return True, @a from can reach @a to; otherwise, false
189  */
190 bool CycleGraph::checkReachable(CycleNode *from, CycleNode *to) const
191 {
192         std::vector< CycleNode *, ModelAlloc<CycleNode *> > queue;
193         discovered->reset();
194
195         queue.push_back(from);
196         discovered->put(from, from);
197         while (!queue.empty()) {
198                 CycleNode *node = queue.back();
199                 queue.pop_back();
200                 if (node == to)
201                         return true;
202
203                 for (unsigned int i = 0; i < node->getNumEdges(); i++) {
204                         CycleNode *next = node->getEdge(i);
205                         if (!discovered->contains(next)) {
206                                 discovered->put(next, next);
207                                 queue.push_back(next);
208                         }
209                 }
210         }
211         return false;
212 }
213
214 bool CycleGraph::checkPromise(const ModelAction *fromact, Promise *promise) const
215 {
216         std::vector< CycleNode *, ModelAlloc<CycleNode *> > queue;
217         discovered->reset();
218         CycleNode *from = actionToNode.get(fromact);
219
220         queue.push_back(from);
221         discovered->put(from, from);
222         while (!queue.empty()) {
223                 CycleNode *node = queue.back();
224                 queue.pop_back();
225
226                 if (promise->increment_threads(node->getAction()->get_tid())) {
227                         return true;
228                 }
229
230                 for (unsigned int i = 0; i < node->getNumEdges(); i++) {
231                         CycleNode *next = node->getEdge(i);
232                         if (!discovered->contains(next)) {
233                                 discovered->put(next, next);
234                                 queue.push_back(next);
235                         }
236                 }
237         }
238         return false;
239 }
240
241 void CycleGraph::startChanges()
242 {
243         ASSERT(rollbackvector.size() == 0);
244         ASSERT(rmwrollbackvector.size() == 0);
245         ASSERT(oldCycles == hasCycles);
246         ASSERT(oldRMWViolation == hasRMWViolation);
247 }
248
249 /** Commit changes to the cyclegraph. */
250 void CycleGraph::commitChanges()
251 {
252         rollbackvector.resize(0);
253         rmwrollbackvector.resize(0);
254         oldCycles = hasCycles;
255         oldRMWViolation = hasRMWViolation;
256 }
257
258 /** Rollback changes to the previous commit. */
259 void CycleGraph::rollbackChanges()
260 {
261         for (unsigned int i = 0; i < rollbackvector.size(); i++) {
262                 rollbackvector[i]->popEdge();
263         }
264
265         for (unsigned int i = 0; i < rmwrollbackvector.size(); i++) {
266                 rmwrollbackvector[i]->clearRMW();
267         }
268
269         hasCycles = oldCycles;
270         hasRMWViolation = oldRMWViolation;
271         rollbackvector.resize(0);
272         rmwrollbackvector.resize(0);
273 }
274
275 /** @returns whether a CycleGraph contains cycles. */
276 bool CycleGraph::checkForCycles() const
277 {
278         return hasCycles;
279 }
280
281 bool CycleGraph::checkForRMWViolation() const
282 {
283         return hasRMWViolation;
284 }
285
286 /**
287  * @brief Constructor for a CycleNode
288  * @param act The ModelAction for this node
289  */
290 CycleNode::CycleNode(const ModelAction *act) :
291         action(act),
292         hasRMW(NULL)
293 {
294 }
295
296 /**
297  * @param i The index of the edge to return
298  * @returns The a CycleNode edge indexed by i
299  */
300 CycleNode * CycleNode::getEdge(unsigned int i) const
301 {
302         return edges[i];
303 }
304
305 /** @returns The number of edges leaving this CycleNode */
306 unsigned int CycleNode::getNumEdges() const
307 {
308         return edges.size();
309 }
310
311 /**
312  * Adds an edge from this CycleNode to another CycleNode.
313  * @param node The node to which we add a directed edge
314  */
315 bool CycleNode::addEdge(CycleNode *node)
316 {
317         for (unsigned int i = 0; i < edges.size(); i++)
318                 if (edges[i] == node)
319                         return false;
320         edges.push_back(node);
321         return true;
322 }
323
324 /** @returns the RMW CycleNode that reads from the current CycleNode */
325 CycleNode * CycleNode::getRMW() const
326 {
327         return hasRMW;
328 }
329
330 /**
331  * Set a RMW action node that reads from the current CycleNode.
332  * @param node The RMW that reads from the current node
333  * @return True, if this node already was read by another RMW; false otherwise
334  * @see CycleGraph::addRMWEdge
335  */
336 bool CycleNode::setRMW(CycleNode *node)
337 {
338         if (hasRMW != NULL)
339                 return true;
340         hasRMW = node;
341         return false;
342 }