Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: TsoChecker.cc | |
4 | // Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
5 | // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
6 | // | |
7 | // The above named program is free software; you can redistribute it and/or | |
8 | // modify it under the terms of the GNU General Public | |
9 | // License version 2 as published by the Free Software Foundation. | |
10 | // | |
11 | // The above named program is distributed in the hope that it will be | |
12 | // useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | // General Public License for more details. | |
15 | // | |
16 | // You should have received a copy of the GNU General Public | |
17 | // License along with this work; if not, write to the Free Software | |
18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | // | |
20 | // ========== Copyright Header End ============================================ | |
21 | /************************************************************************ | |
22 | ** | |
23 | ** Copyright (C) 2002, Sun Microsystems, Inc. | |
24 | ** | |
25 | ** Sun considers its source code as an unpublished, proprietary | |
26 | ** trade secret and it is available only under strict license provisions. | |
27 | ** This copyright notice is placed here only to protect Sun in the event | |
28 | ** the source is deemed a published work. Disassembly, decompilation, | |
29 | ** or other means of reducing the object code to human readable form | |
30 | ** is prohibited by the license agreement under which this code is | |
31 | ** provided to the user or company in possession of this copy." | |
32 | ** | |
33 | *************************************************************************/ | |
34 | #include "TsoChecker.h" | |
35 | #include <sstream> | |
36 | #include <time.h> | |
37 | ||
38 | using namespace std; | |
39 | using namespace Tso; | |
40 | ||
41 | //////////////////////////////////////////////// | |
42 | ||
43 | Tso::TsoChecker::TsoChecker() : | |
44 | listSize_(0), | |
45 | nodeList_(NULL) | |
46 | { | |
47 | fprintf(stdout, "# msync TsoChecker enabled. To disable, use -sas_run_args=-DNO_TSO_CHECKER\n"); | |
48 | } | |
49 | ||
50 | //////////////////////////////////////////////// | |
51 | ||
52 | TsoChecker::TsoChecker( const TsoChecker & orig ) | |
53 | { | |
54 | maccHist_ = orig.maccHist_; | |
55 | } | |
56 | ||
57 | //////////////////////////////////////////////// | |
58 | ||
59 | TsoChecker::~TsoChecker() | |
60 | { | |
61 | ||
62 | } | |
63 | ||
64 | //////////////////////////////////////////////// | |
65 | ||
66 | const TsoChecker & | |
67 | TsoChecker::operator=( const TsoChecker & rhs ) | |
68 | { | |
69 | return *this; | |
70 | } | |
71 | ||
72 | //////////////////////////////////////////////// | |
73 | ||
74 | bool | |
75 | TsoChecker::operator==( const TsoChecker & rhs ) const | |
76 | { | |
77 | return false; | |
78 | } | |
79 | ||
80 | //////////////////////////////////////////////// | |
81 | ||
82 | string | |
83 | TsoChecker::toString() const | |
84 | { | |
85 | ostringstream os; | |
86 | ||
87 | ||
88 | return os.str(); | |
89 | } | |
90 | ||
91 | void | |
92 | TsoChecker::init(int nstrands) | |
93 | { | |
94 | listSize_ = nstrands; | |
95 | nodeList_ = new list<TsoNode*>[nstrands]; | |
96 | tmpList_ = new list<TsoNode*>[nstrands]; | |
97 | lastPoIseq_ = new uint64_t[nstrands]; | |
98 | ||
99 | for (int i = 0; i < nstrands; i++) { | |
100 | lastPoIseq_[i] = 0ull; | |
101 | } | |
102 | } | |
103 | ||
104 | TsoNode* | |
105 | TsoChecker::findNode(TsoCheckerCmd& cmd) | |
106 | { | |
107 | //MSYNC_DEBUG(8, "TsoChecker::findNode cmd=%s", cmd.toString().c_str());//DBX | |
108 | ||
109 | list<TsoNode*>::iterator ni; | |
110 | uint64_t mask; | |
111 | ||
112 | if (tmpList_[cmd.getThrdId()].empty()) { | |
113 | //MSYNC_DEBUG(8, "TsoChecker::findNode tmpList_[%d] empty", cmd.getThrdId());//DBX | |
114 | return (NULL); | |
115 | } | |
116 | ||
117 | mask = ((cmd.getItype() == ITYPE_BLOCK_LOAD) || (cmd.getItype() == ITYPE_BLOCK_STORE)) ? | |
118 | BLK_OP_ADDR_MASK : ((cmd.getItype() == ITYPE_QUAD_LOAD) ? QUAD_OP_ADDR_MASK : FULL_ADDR_MASK); | |
119 | ||
120 | //MSYNC_DEBUG(8, "TsoChecker::findNode mask=%#x", mask);//DBX | |
121 | ||
122 | ni = tmpList_[cmd.getThrdId()].end(); | |
123 | do { | |
124 | ni--; | |
125 | //MSYNC_DEBUG(8, "TsoChecker::findNode ni=%s", (*ni)->toString().c_str());//DBX | |
126 | if ((*ni)->getIseq() == cmd.getIseq() && | |
127 | (*ni)->getItype() == cmd.getItype() && | |
128 | ((*ni)->getAddr() & mask) == (cmd.getAddr()& mask)) { | |
129 | //MSYNC_DEBUG(8, "TsoChecker::findNode match=%s", (*ni)->toString().c_str());//DBX | |
130 | return (*ni); | |
131 | } | |
132 | } while (ni != tmpList_[cmd.getThrdId()].begin()); | |
133 | ||
134 | // if (nodeList_[cmd.getThrdId()].empty()) { | |
135 | // return (NULL); | |
136 | // } | |
137 | // ni = nodeList_[cmd.getThrdId()].end(); | |
138 | // do { | |
139 | // ni--; | |
140 | // if ((*ni)->getIseq() == cmd.getIseq() && | |
141 | // (*ni)->getItype() == cmd.getItype() && | |
142 | // ((*ni)->getAddr() & mask) == (cmd.getAddr() & mask)) { | |
143 | // MS_ASSERT(false, "Should not find node in the nodeList_"); | |
144 | // return (*ni); | |
145 | // } | |
146 | // } while (ni != nodeList_[cmd.getThrdId()].begin()); | |
147 | ||
148 | return (NULL); | |
149 | } | |
150 | ||
151 | TsoNode* | |
152 | TsoChecker::findPrev(enum TSO_NODE_TYPE ntype, uint32_t tid) | |
153 | { | |
154 | list<TsoNode*>::iterator ni; | |
155 | ||
156 | if (nodeList_[tid].empty()) { | |
157 | return (NULL); | |
158 | } | |
159 | ||
160 | ni = nodeList_[tid].end(); | |
161 | do { | |
162 | ni--; | |
163 | switch (ntype) { | |
164 | case TSO_LOAD: | |
165 | if ((*ni)->isTsoLoad()) { | |
166 | return (*ni); | |
167 | } | |
168 | break; | |
169 | case TSO_STORE: | |
170 | if ((*ni)->isTsoStore()) { | |
171 | return (*ni); | |
172 | } | |
173 | break; | |
174 | default: | |
175 | break; | |
176 | } | |
177 | } while (ni != nodeList_[tid].begin()); | |
178 | ||
179 | return (NULL); | |
180 | } | |
181 | ||
182 | void | |
183 | TsoChecker::formDataDependenceEdges(TsoNode *np) | |
184 | { | |
185 | if (np->isTSO()) { | |
186 | if (np->isStoreCommit()) { | |
187 | maccHist_.storeCommit(np); | |
188 | } else if (np->isLoadData()) { | |
189 | if (np->getDsrc() != DSRC_STB) { | |
190 | maccHist_.loadAccess(np); | |
191 | } | |
192 | } | |
193 | } | |
194 | } | |
195 | ||
196 | void | |
197 | TsoChecker::formProgramOrderEdges(uint32_t tid) | |
198 | { | |
199 | list<TsoNode*>::iterator ni; | |
200 | TsoNode *np; | |
201 | TsoEdge edge; | |
202 | ||
203 | for (ni = tmpList_[tid].begin(); ni != tmpList_[tid].end(); ni++) { | |
204 | ||
205 | MSYNC_DEBUG(8, "tmpList %s", (*ni)->toString().c_str()); | |
206 | ||
207 | if ((*ni)->getIseq() != (lastPoIseq_[tid] + 1)) { | |
208 | //MSYNC_DEBUG(8, "TsoChecker iseq not in contiguous sequence, ni=%d, last=%d", (*ni)->getIseq(), (lastPoIseq_[tid] + 1));//DBX | |
209 | break; // not in contiguous sequence | |
210 | } else { | |
211 | lastPoIseq_[tid]++; | |
212 | //MSYNC_DEBUG(8, "TsoChecker lastPoIseq_[%d]=%d", tid, lastPoIseq_[tid]);//DBX | |
213 | } | |
214 | ||
215 | if ((*ni)->isTsoLoad()) { | |
216 | if (np = findPrev(TSO_LOAD, tid)) { | |
217 | edge.addEdge (ET_PROG, np, (*ni)); | |
218 | np->setLastLoad(false); | |
219 | (*ni)->setLastLoad(true); | |
220 | } else { // This can only possible if this is the 1st tsoLoad, not necessary iseq=1 | |
221 | (*ni)->setLastLoad(true); | |
222 | } | |
223 | } | |
224 | if ((*ni)->isTsoStore()) { | |
225 | if (np = findPrev(TSO_LOAD, tid)) { | |
226 | edge.addEdge (ET_PROG, np, (*ni)); | |
227 | } | |
228 | if (np = findPrev(TSO_STORE, tid)) { | |
229 | edge.addEdge (ET_PROG, np, (*ni)); | |
230 | np->setLastStore(false); | |
231 | (*ni)->setLastStore(true); | |
232 | } else { | |
233 | (*ni)->setLastStore(true); | |
234 | } | |
235 | } | |
236 | if ((*ni)->isTSO()) { // atomic is both TsoLoad and TsoStore, but contains only one node | |
237 | nodeList_[tid].push_back(*ni); | |
238 | } | |
239 | } | |
240 | if (ni != tmpList_[tid].end()) { | |
241 | tmpList_[tid].erase(tmpList_[tid].begin(), ni); | |
242 | } | |
243 | } | |
244 | ||
245 | void | |
246 | TsoChecker::input(TsoCheckerCmd& cmd) | |
247 | { | |
248 | /* create Node */ | |
249 | TsoNode* node; // = new TsoNode(cmd); | |
250 | TsoNode* node1; | |
251 | list<TsoNode*>::iterator ni; | |
252 | TsoEdge edge; | |
253 | bool doProgramOrder; | |
254 | bool newNode; | |
255 | ||
256 | uint32_t tid = cmd.getThrdId(); | |
257 | ||
258 | doProgramOrder = false; | |
259 | newNode = false; | |
260 | ||
261 | setTime(cmd.getTime()); | |
262 | ||
263 | if (node = findNode(cmd)) { | |
264 | /******************************************************************************** | |
265 | * This path indicates the instruction corresponds to the node consists of | |
266 | * multiple TSO nodes. The algorithm requires some content of the node been | |
267 | * updated according to the new cmd, and the old content of node been pushed | |
268 | * to the nodes_ list. Doing so, one instruction can appear as one node in | |
269 | * the TSO digraph. | |
270 | ********************************************************************************/ | |
271 | switch (cmd.getItype()) { | |
272 | node1 = new TsoNode(*node); | |
273 | case ITYPE_ATOMIC: | |
274 | // this one must be the store part of the atomic, change content to SC | |
275 | if (cmd.getCmd() != MEM_STORE_COMMIT) | |
276 | { | |
277 | MS_ERROR("Atomic does not follow load-store order. tid=%d PA=%llx", tid, cmd.getAddr()); | |
278 | return; | |
279 | } | |
280 | node->setCommit(cmd.getMacc()); | |
281 | node->setGobs(cmd.getGobs()); | |
282 | node->setData(cmd.getData()); | |
283 | node->setCmd(cmd.getCmd()); | |
284 | node->setSizeV(cmd.getSizeV()); | |
285 | node->setOld(0); | |
286 | node->nodePush(node1); | |
287 | node->incNcnt(); | |
288 | break; | |
289 | case ITYPE_QUAD_LOAD: | |
290 | node->setCommit(cmd.getMacc()); | |
291 | node->setData(cmd.getData()); | |
292 | node->setAddr(cmd.getAddr()); | |
293 | node->setSizeV(cmd.getSizeV()); | |
294 | node->nodePush(node1); | |
295 | node->incNcnt(); | |
296 | break; | |
297 | default: | |
298 | MS_ERROR("Encountered instruction type that should have only one TSO node. tid=%d PA=%llx", tid, cmd.getAddr()); | |
299 | return; | |
300 | } | |
301 | } else { // not atomic or atomic but new | |
302 | node = new TsoNode(cmd); | |
303 | if (cmd.isAtomic()) { | |
304 | MSYNC_DEBUG(8, "atomic-node=%s", node->toString().c_str()); | |
305 | if (cmd.getCmd() != MEM_LOAD_DATA) | |
306 | { | |
307 | MS_ERROR("Atomic node creation should go with LoadData. tid=%d PA=%llx", tid, cmd.getAddr()); | |
308 | return; | |
309 | } | |
310 | } | |
311 | newNode = true; | |
312 | } | |
313 | ||
314 | // if ((cmd.isAtomic() && cmd.isStoreCommit()) || | |
315 | // !cmd.isAtomic()) { | |
316 | if (node->allEntriesArrive()) { | |
317 | doProgramOrder = (node->getIseq() == (lastPoIseq_[tid] + 1)) ? true : false; | |
318 | } | |
319 | ||
320 | MSYNC_DEBUG(6, "node=%s", node->toString().c_str()); | |
321 | ||
322 | /* Note that the node's entry type is not valid for atomic instruction | |
323 | whose LoadData and StoreCommit share the same node. Therefore, the | |
324 | entry type is always LoadData. That is why the MemoryAccessHistory | |
325 | action uses cmd.isStoreCommit() and cmd.isLoadData() for conditional | |
326 | check. An atomic instruction that has both load and store parts should | |
327 | perform both maccHist_.storeCommit() and maccHist_.loadAccess(). | |
328 | ||
329 | Since an atomic node can be processed in both if-else parts, the | |
330 | program order edge may be formed to times. The edge.addEdge() | |
331 | will detect this, and force only one edge is formed. | |
332 | */ | |
333 | ||
334 | /* note that the iseq coming to this function could be out of order */ | |
335 | ||
336 | ||
337 | /* Form data dependence edges */ | |
338 | formDataDependenceEdges(node); | |
339 | ||
340 | if (newNode) { | |
341 | tmpListInsert (tid, node); | |
342 | MSYNC_DEBUG(6, "tmpList size=%d %s", | |
343 | tmpList_[tid].size(), node->toString().c_str()); | |
344 | } | |
345 | ||
346 | /* Form program order edges */ | |
347 | if (doProgramOrder) { | |
348 | if (tmpList_[tid].empty()) | |
349 | { | |
350 | MS_ERROR("tmpList cannot be empty. tid=%d PA=%llx", tid, cmd.getAddr()); | |
351 | return; | |
352 | } | |
353 | formProgramOrderEdges(tid); | |
354 | } | |
355 | ||
356 | /* Cycle check and prun nodes */ | |
357 | ||
358 | bool removed; | |
359 | do { | |
360 | removed = false; | |
361 | for (ni = nodeList_[tid].begin(); ni != nodeList_[tid].end(); ni++) { | |
362 | MSYNC_DEBUG(6, "Check %s", (*ni)->toString().c_str()); | |
363 | if (canRemove(*ni)) { | |
364 | if (checkCycle(*ni, *ni)) { | |
365 | MSYNC_DEBUG(0, "Tso Violation: %s", (*ni)->toString().c_str()); | |
366 | printNodeList(ERROR); | |
367 | MemorySyncMessage::error("Tso Violation"); | |
368 | } else { // no TSO violation | |
369 | removeNode(*ni); | |
370 | nodeList_[tid].erase(ni); | |
371 | removed = true; | |
372 | // delete(*ni); // need to make sure the node does not appear in other list | |
373 | break; | |
374 | } | |
375 | } | |
376 | } | |
377 | } while (!nodeList_[tid].empty() && removed); | |
378 | ||
379 | } | |
380 | ||
381 | void | |
382 | TsoChecker::input(uint32_t tid, uint64_t iseq, enum INSTR_TYPE itype, enum MEM_CMD cmd, | |
383 | uint64_t addr, uint64_t data, uint8_t sizeV, enum DATA_SRC dsrc, | |
384 | uint64_t macc, uint64_t gobs, uint64_t time) | |
385 | { | |
386 | TsoCheckerCmd tsocmd; | |
387 | ||
388 | tsocmd.setIseq(iseq); | |
389 | tsocmd.setMacc(macc); | |
390 | tsocmd.setGobs(gobs); | |
391 | tsocmd.setThrdId(tid); | |
392 | tsocmd.setItype(itype); | |
393 | tsocmd.setCmd(cmd); | |
394 | tsocmd.setAddr(addr); | |
395 | tsocmd.setData(data); | |
396 | tsocmd.setDsrc(dsrc); | |
397 | tsocmd.setSizeV(sizeV); | |
398 | tsocmd.setTime(time); | |
399 | ||
400 | MSYNC_DEBUG(6, "tsocmd=%s", tsocmd.toString().c_str()); | |
401 | ||
402 | input(tsocmd); | |
403 | } | |
404 | ||
405 | string | |
406 | TsoChecker::printOutEdges(TsoNode* node) { | |
407 | ||
408 | ostringstream os; | |
409 | list<TsoEdge*>::iterator ei; | |
410 | ||
411 | // os << "nodePtr=" << node << endl; | |
412 | for (ei = node->outBegin(); ei != node->outEnd(); ei++) { | |
413 | os << " " << (char*) node->getNodeName() << " -> " << (char*) (*ei)->getDst()->getNodeName(); | |
414 | os << " [label=" << '"' << edgeType[(*ei)->getType()] << '"' << "]" << ";" << endl; | |
415 | } | |
416 | ||
417 | return os.str(); | |
418 | } | |
419 | ||
420 | void | |
421 | TsoChecker::printNodeList() | |
422 | { | |
423 | printNodeList(NORMAL); | |
424 | } | |
425 | ||
426 | void | |
427 | TsoChecker::printNodeList(int flag) | |
428 | { | |
429 | list<TsoNode*>::iterator ni; | |
430 | TsoNode* nodePtr; | |
431 | ofstream out; | |
432 | ||
433 | if (flag == ERROR) { | |
434 | out.open("TsoViolationDiGraph"); | |
435 | } else { | |
436 | out.open("TsoDiGraph"); | |
437 | } | |
438 | if (!out) { | |
439 | cout << "Cannot Open File" << endl; | |
440 | } | |
441 | ||
442 | // cerr << "TsoChecker is printing TsoDiGraph" << endl; | |
443 | ||
444 | out << "digraph G {" << endl; | |
445 | out << " center=true;" << endl; | |
446 | out << " size=" << '"' << "7.5, 10" << '"' << ";" << endl; | |
447 | //out << "DBX: listSize=" << listSize_ << endl;//DBX | |
448 | int i; | |
449 | //for (i = 0; i < MAX_STRANDS; i++) { | |
450 | for (i = 0; i < listSize_; i++) { | |
451 | //out << "DBX: nodeList_[" << i << "]=" << &(nodeList_[i]) << endl;//DBX | |
452 | out << "# nodelist " << i << " size=" << nodeList_[i].size() << endl; | |
453 | } | |
454 | //for (i = 0; i < MAX_STRANDS; i++) { | |
455 | for (i = 0; i < listSize_; i++) { | |
456 | if (nodeList_[i].size() > 0) { | |
457 | out << " subgraph cluster" << i << " {" << endl; | |
458 | out << " label = " << '"' << "thread " << i << '"' << ";" << endl; | |
459 | for (ni = nodeList_[i].begin(); ni != nodeList_[i].end(); ni++) { | |
460 | out << " " << (char*) (*ni)->getNodeName(); | |
461 | out << " [label=" << '"' << (*ni)->getIseq() << "," << mmitype[(*ni)->getItype()] << '"' << "];" << endl; | |
462 | MSYNC_DEBUG(6, "NodeList %s", (*ni)->toString().c_str()); | |
463 | } | |
464 | out << " }" << endl; | |
465 | } | |
466 | } | |
467 | ||
468 | //for (i = 0; i < MAX_STRANDS; i++) { | |
469 | for (i = 0; i < listSize_; i++) { | |
470 | if (nodeList_[i].size() > 0) { | |
471 | for (ni = nodeList_[i].begin(); ni != nodeList_[i].end(); ni++) { | |
472 | out << printOutEdges (*ni); | |
473 | } | |
474 | } | |
475 | } | |
476 | ||
477 | out << "}" << endl; | |
478 | ||
479 | } | |
480 | ||
481 | bool | |
482 | TsoChecker::checkCycle(TsoNode *np, TsoNode *start_np) | |
483 | { | |
484 | list<TsoEdge*>::iterator ei; | |
485 | TsoEdge *ep; | |
486 | TsoNode *np1; | |
487 | ||
488 | if (np->noInEdges()) return false; | |
489 | ||
490 | for (ei = np->inBegin(); ei != np->inEnd(); ei++) { | |
491 | ep = *ei; | |
492 | np1 = ep->getSrc(); | |
493 | if (np1 == start_np) { | |
494 | MSYNC_DEBUG(0, "Tso Violation: %s", np1->toString().c_str()); | |
495 | return true; | |
496 | } else { | |
497 | if (checkCycle(np1, start_np)) { | |
498 | MSYNC_DEBUG(0, "Tso Violation: %s", np1->toString().c_str()); | |
499 | return true; | |
500 | } | |
501 | } | |
502 | } | |
503 | return false; | |
504 | } | |
505 | ||
506 | bool | |
507 | TsoChecker::canRemove(TsoNode* np) | |
508 | { | |
509 | // return false; | |
510 | // if (np->getIseq() >= 20) return false; | |
511 | ||
512 | bool storeOk = false; | |
513 | bool loadOk = false; | |
514 | ||
515 | if (np->isStore() && !np->isLastStore() && | |
516 | (np->isAllOld() && (np->getRetire() <= getTime()))) { | |
517 | storeOk = true; | |
518 | } | |
519 | if (np->isLoad() && !np->isLastLoad()) { | |
520 | loadOk = true; | |
521 | } | |
522 | ||
523 | if (np->isAtomic()) { | |
524 | return (storeOk && loadOk); | |
525 | } else { | |
526 | return (storeOk || loadOk); | |
527 | } | |
528 | } | |
529 | ||
530 | void | |
531 | TsoChecker::removeNode(TsoNode* np) | |
532 | { | |
533 | // MS_ASSERT(np->noInEdges(), "Node that is to be removed has in-edges"); | |
534 | ||
535 | MSYNC_DEBUG(6, "Remove %s", np->toString().c_str()); | |
536 | if (np->isStore()) { | |
537 | maccHist_.removeStoreNode(np); | |
538 | } | |
539 | np->removeEdges(); | |
540 | } | |
541 | ||
542 | void | |
543 | TsoChecker::tmpListInsert(uint32_t tid, TsoNode *np) | |
544 | { | |
545 | list<TsoNode*>::iterator ni; | |
546 | list<TsoNode*>::iterator posi; // insert position iterator | |
547 | ||
548 | if (tmpList_[tid].empty()) { | |
549 | tmpList_[tid].push_back(np); | |
550 | return; | |
551 | } | |
552 | ||
553 | for (ni = tmpList_[tid].begin(); ni != tmpList_[tid].end(); ni++) { | |
554 | if ((*ni)->getIseq() > np->getIseq()) { | |
555 | tmpList_[tid].insert(ni, np); | |
556 | return; | |
557 | } | |
558 | } | |
559 | ||
560 | tmpList_[tid].push_back(np); | |
561 | return; | |
562 | } |