scanalysis: don't rely on greedy search
[cdsspec-compiler.git] / scanalysis.cc
1 #include "scanalysis.h"
2 #include "action.h"
3 #include "threads-model.h"
4 #include "clockvector.h"
5 #include "execution.h"
6
7 SCAnalysis::SCAnalysis(const ModelExecution *execution) :
8         cvmap(),
9         cyclic(false),
10         badrfset(),
11         lastwrmap(),
12         threadlists(1),
13         execution(execution)
14 {
15 }
16
17 SCAnalysis::~SCAnalysis() {
18 }
19
20 void SCAnalysis::print_list(action_list_t *list) {
21         model_print("---------------------------------------------------------------------\n");
22         if (cyclic)
23                 model_print("Not SC\n");
24         unsigned int hash = 0;
25
26         for (action_list_t::iterator it = list->begin(); it != list->end(); it++) {
27                 const ModelAction *act = *it;
28                 if (act->get_seq_number() > 0) {
29                         if (badrfset.contains(act))
30                                 model_print("BRF ");
31                         act->print();
32                         if (badrfset.contains(act)) {
33                                 model_print("Desired Rf: %u \n",badrfset.get(act)->get_seq_number());
34                         }
35                 }
36                 hash = hash ^ (hash << 3) ^ ((*it)->hash());
37         }
38         model_print("HASH %u\n", hash);
39         model_print("---------------------------------------------------------------------\n");
40 }
41
42 void SCAnalysis::analyze(action_list_t *actions) {
43         action_list_t *list = generateSC(actions);
44         check_rf(list);
45         print_list(list);
46 }
47
48 void SCAnalysis::check_rf(action_list_t *list) {
49         for (action_list_t::iterator it = list->begin(); it != list->end(); it++) {
50                 const ModelAction *act = *it;
51                 if (act->is_read()) {
52                         if (act->get_reads_from()!=lastwrmap.get(act->get_location()))
53                                 badrfset.put(act,lastwrmap.get(act->get_location()));
54                 }
55                 if (act->is_write())
56                         lastwrmap.put(act->get_location(), act);
57         }
58 }
59
60 bool SCAnalysis::merge(ClockVector *cv, const ModelAction *act, const ModelAction *act2) {
61         ClockVector * cv2=cvmap.get(act2);
62         if (cv2==NULL)
63                 return true;
64         if (cv2->getClock(act->get_tid()) >= act->get_seq_number() && act->get_seq_number() != 0) {
65                 cyclic=true;
66                 //refuse to introduce cycles into clock vectors
67                 return false;
68         }
69
70         return cv->merge(cv2);
71 }
72
73 int SCAnalysis::getNextActions(ModelAction ** array) {
74         int count=0;
75
76         for (int t = 0; t <= maxthreads; t++) {
77                 action_list_t *tlt = &threadlists[t];
78                 if (tlt->empty())
79                         continue;
80                 ModelAction *act = tlt->front();
81                 ClockVector *cv = cvmap.get(act);
82                 
83                 /* Find the earliest in SC ordering */
84                 for (int i = 0; i <= maxthreads; i++) {
85                         if ( i == t )
86                                 continue;
87                         action_list_t *threadlist = &threadlists[i];
88                         if (threadlist->empty())
89                                 continue;
90                         ModelAction *first = threadlist->front();
91                         if (cv->synchronized_since(first)) {
92                                 act = NULL;
93                                 break;
94                         }
95                 }
96                 if (act != NULL) {
97                         array[count++]=act;
98                 }
99         }
100         if (count != 0)
101                 return count;
102         for (int t = 0; t <= maxthreads; t++) {
103                 action_list_t *tlt = &threadlists[t];
104                 if (tlt->empty())
105                         continue;
106                 ModelAction *act = tlt->front();
107                 ClockVector *cv = act->get_cv();
108                 
109                 /* Find the earliest in SC ordering */
110                 for (int i = 0; i <= maxthreads; i++) {
111                         if ( i == t )
112                                 continue;
113                         action_list_t *threadlist = &threadlists[i];
114                         if (threadlist->empty())
115                                 continue;
116                         ModelAction *first = threadlist->front();
117                         if (cv->synchronized_since(first)) {
118                                 act = NULL;
119                                 break;
120                         }
121                 }
122                 if (act != NULL) {
123                         array[count++]=act;
124                 }
125         }
126
127         ASSERT(count==0 || cyclic);
128
129         return count;
130 }
131
132 ModelAction * SCAnalysis::pruneArray(ModelAction **array,int count) {
133         /* No choice */
134         if (count == 1)
135                 return array[0];
136
137         /* Choose first non-write action */
138         ModelAction *nonwrite=NULL;
139         for(int i=0;i<count;i++) {
140                 if (!array[i]->is_write())
141                         if (nonwrite==NULL || nonwrite->get_seq_number() > array[i]->get_seq_number())
142                                 nonwrite = array[i];
143         }
144         if (nonwrite != NULL)
145                 return nonwrite;
146         
147         /* Look for non-conflicting action */
148         ModelAction *nonconflict=NULL;
149         for(int a=0;a<count;a++) {
150                 ModelAction *act=array[a];
151                 for (int i = 0; i <= maxthreads && act != NULL; i++) {
152                         thread_id_t tid = int_to_id(i);
153                         if (tid == act->get_tid())
154                                 continue;
155                         
156                         action_list_t *list = &threadlists[id_to_int(tid)];
157                         for (action_list_t::iterator rit = list->begin(); rit != list->end(); rit++) {
158                                 ModelAction *write = *rit;
159                                 if (!write->is_write())
160                                         continue;
161                                 ClockVector *writecv = cvmap.get(write);
162                                 if (writecv->synchronized_since(act))
163                                         break;
164                                 if (write->get_location() == act->get_location()) {
165                                         //write is sc after act
166                                         act = NULL;
167                                         break;
168                                 }
169                         }
170                 }
171                 if (act != NULL) {
172                         if (nonconflict == NULL || nonconflict->get_seq_number() > act->get_seq_number())
173                                 nonconflict=act;
174                 }
175         }
176         return nonconflict;
177 }
178
179 action_list_t * SCAnalysis::generateSC(action_list_t *list) {
180         int numactions=buildVectors(list);
181         computeCV(list);
182
183         action_list_t *sclist = new action_list_t();
184         ModelAction **array = (ModelAction **)model_calloc(1, (maxthreads + 1) * sizeof(ModelAction *));
185         int * choices = (int *) model_calloc(1, sizeof(int)*numactions);
186         int endchoice = 0;
187         int currchoice = 0;
188         int lastchoice = -1;
189         while (true) {
190                 int numActions = getNextActions(array);
191                 if (numActions == 0)
192                         break;
193                 ModelAction * act=pruneArray(array, numActions);
194                 if (act == NULL) {
195                         if (currchoice < endchoice) {
196                                 act = array[choices[currchoice]];
197                                 //check whether there is still another option
198                                 if ((choices[currchoice]+1)<numActions)
199                                         lastchoice=currchoice;
200                                 currchoice++;
201                         } else {
202                                 act = array[0];
203                                 choices[currchoice]=0;
204                                 if (numActions>1)
205                                         lastchoice=currchoice;
206                                 currchoice++;
207                         }
208                 }
209                 thread_id_t tid = act->get_tid();
210                 //remove action
211                 threadlists[id_to_int(tid)].pop_front();
212                 //add ordering constraints from this choice
213                 if (updateConstraints(act)) {
214                         //propagate changes if we have them
215                         bool prevc=cyclic;
216                         computeCV(list);
217                         if (!prevc && cyclic) {
218                                 model_print("ROLLBACK in SC\n");
219                                 //check whether we have another choice
220                                 if (lastchoice != -1) {
221                                         //have to reset everything
222                                         choices[lastchoice]++;
223                                         endchoice=lastchoice+1;
224                                         currchoice=0;
225                                         lastchoice=-1;
226                                         reset(list);
227                                         buildVectors(list);
228                                         computeCV(list);
229                                         sclist->clear();
230                                         continue;
231                                 }
232                         }
233                 }
234                 //add action to end
235                 sclist->push_back(act);
236         }
237         model_free(array);
238         return sclist;
239 }
240
241 int SCAnalysis::buildVectors(action_list_t *list) {
242         maxthreads = 0;
243         int numactions = 0;
244         for (action_list_t::iterator it = list->begin(); it != list->end(); it++) {
245                 ModelAction *act = *it;
246                 numactions++;
247                 int threadid = id_to_int(act->get_tid());
248                 if (threadid > maxthreads) {
249                         threadlists.resize(threadid + 1);
250                         maxthreads = threadid;
251                 }
252                 threadlists[threadid].push_back(act);
253         }
254         return numactions;
255 }
256
257 void SCAnalysis::reset(action_list_t *list) {
258         for (int t = 0; t <= maxthreads; t++) {
259                 action_list_t *tlt = &threadlists[t];
260                 tlt->clear();
261         }
262         for (action_list_t::iterator it = list->begin(); it != list->end(); it++) {
263                 ModelAction *act = *it;
264                 delete cvmap.get(act);
265                 cvmap.put(act, NULL);
266         }
267
268         cyclic=false;   
269 }
270
271 bool SCAnalysis::updateConstraints(ModelAction *act) {
272         bool changed = false;
273         for (int i = 0; i <= maxthreads; i++) {
274                 thread_id_t tid = int_to_id(i);
275                 if (tid == act->get_tid())
276                         continue;
277
278                 action_list_t *list = &threadlists[id_to_int(tid)];
279                 for (action_list_t::iterator rit = list->begin(); rit != list->end(); rit++) {
280                         ModelAction *write = *rit;
281                         if (!write->is_write())
282                                 continue;
283                         ClockVector *writecv = cvmap.get(write);
284                         if (writecv->synchronized_since(act))
285                                 break;
286                         if (write->get_location() == act->get_location()) {
287                                 //write is sc after act
288                                 merge(writecv, write, act);
289                                 changed = true;
290                                 break;
291                         }
292                 }
293         }
294         return changed;
295 }
296
297 bool SCAnalysis::processRead(ModelAction *read, ClockVector *cv) {
298         bool changed = false;
299
300         /* Merge in the clock vector from the write */
301         const ModelAction *write = read->get_reads_from();
302         ClockVector *writecv = cvmap.get(write);
303         changed |= merge(cv, read, write) && (*read < *write);
304
305         for (int i = 0; i <= maxthreads; i++) {
306                 thread_id_t tid = int_to_id(i);
307                 if (tid == read->get_tid())
308                         continue;
309                 if (tid == write->get_tid())
310                         continue;
311                 action_list_t *list = execution->get_actions_on_obj(read->get_location(), tid);
312                 if (list == NULL)
313                         continue;
314                 for (action_list_t::reverse_iterator rit = list->rbegin(); rit != list->rend(); rit++) {
315                         ModelAction *write2 = *rit;
316                         if (!write2->is_write())
317                                 continue;
318
319                         ClockVector *write2cv = cvmap.get(write2);
320                         if (write2cv == NULL)
321                                 continue;
322
323                         /* write -sc-> write2 &&
324                                  write -rf-> R =>
325                                  R -sc-> write2 */
326                         if (write2cv->synchronized_since(write)) {
327                                 changed |= merge(write2cv, write2, read);
328                         }
329
330                         //looking for earliest write2 in iteration to satisfy this
331                         /* write2 -sc-> R &&
332                                  write -rf-> R =>
333                                  write2 -sc-> write */
334                         if (cv->synchronized_since(write2)) {
335                                 changed |= writecv==NULL || merge(writecv, write, write2);
336                                 break;
337                         }
338                 }
339         }
340         return changed;
341 }
342
343 void SCAnalysis::computeCV(action_list_t *list) {
344         bool changed = true;
345         bool firsttime = true;
346         ModelAction **last_act = (ModelAction **)model_calloc(1, (maxthreads + 1) * sizeof(ModelAction *));
347         while (changed) {
348                 changed = changed&firsttime;
349                 firsttime = false;
350
351                 for (action_list_t::iterator it = list->begin(); it != list->end(); it++) {
352                         ModelAction *act = *it;
353                         ModelAction *lastact = last_act[id_to_int(act->get_tid())];
354                         if (act->is_thread_start())
355                                 lastact = execution->get_thread(act)->get_creation();
356                         last_act[id_to_int(act->get_tid())] = act;
357                         ClockVector *cv = cvmap.get(act);
358                         if (cv == NULL) {
359                                 cv = new ClockVector(NULL, act);
360                                 cvmap.put(act, cv);
361                         }
362                         if (lastact != NULL) {
363                                 merge(cv, act, lastact);
364                         }
365                         if (act->is_thread_join()) {
366                                 Thread *joinedthr = act->get_thread_operand();
367                                 ModelAction *finish = execution->get_last_action(joinedthr->get_id());
368                                 changed |= merge(cv, act, finish);
369                         }
370                         if (act->is_read()) {
371                                 changed |= processRead(act, cv);
372                         }
373                 }
374                 /* Reset the last action array */
375                 if (changed) {
376                         bzero(last_act, (maxthreads + 1) * sizeof(ModelAction *));
377                 }
378         }
379         model_free(last_act);
380 }