ruby: Ruby support for sparse memory
[gem5.git] / src / mem / slicc / symbols / Type.py
1 # Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
2 # Copyright (c) 2009 The Hewlett-Packard Development Company
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met: redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer;
9 # redistributions in binary form must reproduce the above copyright
10 # notice, this list of conditions and the following disclaimer in the
11 # documentation and/or other materials provided with the distribution;
12 # neither the name of the copyright holders nor the names of its
13 # contributors may be used to endorse or promote products derived from
14 # this software without specific prior written permission.
15 #
16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 from m5.util import orderdict
29
30 from slicc.util import PairContainer
31 from slicc.symbols.Symbol import Symbol
32
33 class DataMember(PairContainer):
34 def __init__(self, ident, type, pairs, init_code):
35 super(DataMember, self).__init__(pairs)
36 self.ident = ident
37 self.type = type
38 self.init_code = init_code
39
40 class Enumeration(PairContainer):
41 def __init__(self, ident, pairs):
42 super(Enumeration, self).__init__(pairs)
43 self.ident = ident
44
45 class Method(object):
46 def __init__(self, return_type, param_types):
47 self.return_type = return_type
48 self.param_types = param_types
49
50 class Type(Symbol):
51 def __init__(self, table, ident, location, pairs, machine=None):
52 super(Type, self).__init__(table, ident, location, pairs)
53 self.c_ident = ident
54 self.abstract_ident = ""
55 if machine:
56 if self.isExternal or self.isPrimitive:
57 if "external_name" in self:
58 self.c_ident = self["external_name"]
59 else:
60 # Append with machine name
61 self.c_ident = "%s_%s" % (machine, ident)
62
63 self.pairs.setdefault("desc", "No description avaliable")
64
65 # check for interface that this Type implements
66 if "interface" in self:
67 interface = self["interface"]
68 if interface in ("Message", "NetworkMessage"):
69 self["message"] = "yes"
70 if interface == "NetworkMessage":
71 self["networkmessage"] = "yes"
72
73 # FIXME - all of the following id comparisons are fragile hacks
74 if self.ident in ("CacheMemory", "NewCacheMemory",
75 "TLCCacheMemory", "DNUCACacheMemory",
76 "DNUCABankCacheMemory", "L2BankCacheMemory",
77 "CompressedCacheMemory", "PrefetchCacheMemory"):
78 self["cache"] = "yes"
79
80 if self.ident in ("TBETable", "DNUCATBETable", "DNUCAStopTable"):
81 self["tbe"] = "yes"
82
83 if self.ident == "NewTBETable":
84 self["newtbe"] = "yes"
85
86 if self.ident == "TimerTable":
87 self["timer"] = "yes"
88
89 if self.ident == "DirectoryMemory":
90 self["dir"] = "yes"
91
92 if self.ident == "PersistentTable":
93 self["persistent"] = "yes"
94
95 if self.ident == "Prefetcher":
96 self["prefetcher"] = "yes"
97
98 if self.ident == "DNUCA_Movement":
99 self["mover"] = "yes"
100
101 self.isMachineType = (ident == "MachineType")
102
103 self.data_members = orderdict()
104
105 # Methods
106 self.methods = {}
107
108 # Enums
109 self.enums = orderdict()
110
111 @property
112 def isPrimitive(self):
113 return "primitive" in self
114 @property
115 def isNetworkMessage(self):
116 return "networkmessage" in self
117 @property
118 def isMessage(self):
119 return "message" in self
120 @property
121 def isBuffer(self):
122 return "buffer" in self
123 @property
124 def isInPort(self):
125 return "inport" in self
126 @property
127 def isOutPort(self):
128 return "outport" in self
129 @property
130 def isEnumeration(self):
131 return "enumeration" in self
132 @property
133 def isExternal(self):
134 return "external" in self
135 @property
136 def isGlobal(self):
137 return "global" in self
138 @property
139 def isInterface(self):
140 return "interface" in self
141
142 # Return false on error
143 def dataMemberAdd(self, ident, type, pairs, init_code):
144 if ident in self.data_members:
145 return False
146
147 member = DataMember(ident, type, pairs, init_code)
148 self.data_members[ident] = member
149
150 return True
151
152 def dataMemberType(self, ident):
153 return self.data_members[ident].type
154
155 def methodId(self, name, param_type_vec):
156 return '_'.join([name] + [ pt.c_ident for pt in param_type_vec ])
157
158 def methodIdAbstract(self, name, param_type_vec):
159 return '_'.join([name] + [ pt.abstract_ident for pt in param_type_vec ])
160
161 def methodAdd(self, name, return_type, param_type_vec):
162 ident = self.methodId(name, param_type_vec)
163 if ident in self.methods:
164 return False
165
166 self.methods[ident] = Method(return_type, param_type_vec)
167 return True
168
169 def enumAdd(self, ident, pairs):
170 if ident in self.enums:
171 return False
172
173 self.enums[ident] = Enumeration(ident, pairs)
174
175 # Add default
176 if "default" not in self:
177 self["default"] = "%s_NUM" % self.c_ident
178
179 return True
180
181 def writeCodeFiles(self, path):
182 if self.isExternal:
183 # Do nothing
184 pass
185 elif self.isEnumeration:
186 self.printEnumHH(path)
187 self.printEnumCC(path)
188 else:
189 # User defined structs and messages
190 self.printTypeHH(path)
191 self.printTypeCC(path)
192
193 def printTypeHH(self, path):
194 code = self.symtab.codeFormatter()
195 code('''
196 /** \\file ${{self.c_ident}}.hh
197 *
198 *
199 * Auto generated C++ code started by $__file__:$__line__
200 */
201
202 #ifndef __${{self.c_ident}}_HH__
203 #define __${{self.c_ident}}_HH__
204
205 #include <iostream>
206
207 #include "mem/ruby/common/Global.hh"
208 #include "mem/gems_common/Allocator.hh"
209 ''')
210
211 for dm in self.data_members.values():
212 if not dm.type.isPrimitive:
213 code('#include "mem/protocol/$0.hh"', dm.type.c_ident)
214
215 parent = ""
216 if "interface" in self:
217 code('#include "mem/protocol/$0.hh"', self["interface"])
218 parent = " : public %s" % self["interface"]
219
220 code('''
221 $klass ${{self.c_ident}}$parent
222 {
223 public:
224 ${{self.c_ident}}()
225 {
226 ''', klass="class")
227
228 code.indent()
229 if not self.isGlobal:
230 code.indent()
231 for dm in self.data_members.values():
232 ident = dm.ident
233 if "default" in dm:
234 # look for default value
235 code('m_$ident = ${{dm["default"]}}; // default for this field')
236 elif "default" in dm.type:
237 # Look for the type default
238 tid = dm.type.c_ident
239 code('m_$ident = ${{dm.type["default"]}}; // default value of $tid')
240 else:
241 code('// m_$ident has no default')
242 code.dedent()
243 code('}')
244
245 # ******** Full init constructor ********
246 if not self.isGlobal:
247 params = [ 'const %s& local_%s' % (dm.type.c_ident, dm.ident) \
248 for dm in self.data_members.itervalues() ]
249
250 if self.isMessage:
251 params.append('const unsigned local_proc_id')
252
253 params = ', '.join(params)
254 code('${{self.c_ident}}($params)')
255
256 # Call superclass constructor
257 if "interface" in self:
258 code(' : ${{self["interface"]}}()')
259
260 code('{')
261 code.indent()
262 for dm in self.data_members.values():
263 code('m_${{dm.ident}} = local_${{dm.ident}};')
264 if "nextLineCallHack" in dm:
265 code('m_${{dm.ident}}${{dm["nextLineCallHack"]}};')
266
267 if self.isMessage:
268 code('proc_id = local_proc_id;')
269
270 code.dedent()
271 code('}')
272
273 # create a static factory method
274 if "interface" in self:
275 code('''
276 static ${{self["interface"]}}*
277 create()
278 {
279 return new ${{self.c_ident}}();
280 }
281 ''')
282
283 # ******** Message member functions ********
284 # FIXME: those should be moved into slicc file, slicc should
285 # support more of the c++ class inheritance
286
287 if self.isMessage:
288 code('''
289 Message *
290 clone() const
291 {
292 checkAllocator();
293 return s_allocator_ptr->allocate(*this);
294 }
295
296 void
297 destroy()
298 {
299 checkAllocator();
300 s_allocator_ptr->deallocate(this);
301 }
302
303 static Allocator<${{self.c_ident}}>* s_allocator_ptr;
304
305 static void
306 checkAllocator()
307 {
308 if (s_allocator_ptr == NULL) {
309 s_allocator_ptr = new Allocator<${{self.c_ident}}>;
310 }
311 }
312 ''')
313
314 if not self.isGlobal:
315 # const Get methods for each field
316 code('// Const accessors methods for each field')
317 for dm in self.data_members.values():
318 code('''
319 /** \\brief Const accessor method for ${{dm.ident}} field.
320 * \\return ${{dm.ident}} field
321 */
322 const ${{dm.type.c_ident}}&
323 get${{dm.ident}}() const
324 {
325 return m_${{dm.ident}};
326 }
327 ''')
328
329 # Non-const Get methods for each field
330 code('// Non const Accessors methods for each field')
331 for dm in self.data_members.values():
332 code('''
333 /** \\brief Non-const accessor method for ${{dm.ident}} field.
334 * \\return ${{dm.ident}} field
335 */
336 ${{dm.type.c_ident}}&
337 get${{dm.ident}}()
338 {
339 return m_${{dm.ident}};
340 }
341 ''')
342
343 #Set methods for each field
344 code('// Mutator methods for each field')
345 for dm in self.data_members.values():
346 code('''
347 /** \\brief Mutator method for ${{dm.ident}} field */
348 void
349 set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}})
350 {
351 m_${{dm.ident}} = local_${{dm.ident}};
352 }
353 ''')
354
355 code('void print(std::ostream& out) const;')
356 code.dedent()
357 code(' //private:')
358 code.indent()
359
360 # Data members for each field
361 for dm in self.data_members.values():
362 if "abstract" not in dm:
363 const = ""
364 init = ""
365
366 # global structure
367 if self.isGlobal:
368 const = "static const "
369
370 # init value
371 if dm.init_code:
372 # only global structure can have init value here
373 assert self.isGlobal
374 init = " = %s" % (dm.init_code)
375
376 if "desc" in dm:
377 code('/** ${{dm["desc"]}} */')
378
379 code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init;')
380
381 if self.isMessage:
382 code('unsigned proc_id;')
383
384 code.dedent()
385 code('};')
386
387 code('''
388 // Output operator declaration
389 std::ostream&
390 operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
391
392 // Output operator definition
393 extern inline std::ostream&
394 operator<<(std::ostream& out, const ${{self.c_ident}}& obj)
395 {
396 obj.print(out);
397 out << std::flush;
398 return out;
399 }
400
401 #endif // __${{self.c_ident}}_HH__
402 ''')
403
404 code.write(path, "%s.hh" % self.c_ident)
405
406 def printTypeCC(self, path):
407 code = self.symtab.codeFormatter()
408
409 code('''
410 /** \\file ${{self.c_ident}}.cc
411 *
412 * Auto generated C++ code started by $__file__:$__line__
413 */
414
415 #include <iostream>
416
417 #include "mem/protocol/${{self.c_ident}}.hh"
418
419 using namespace std;
420 ''')
421
422 if self.isMessage:
423 code('Allocator<${{self.c_ident}}>* ${{self.c_ident}}::s_allocator_ptr = NULL;')
424 code('''
425 /** \\brief Print the state of this object */
426 void
427 ${{self.c_ident}}::print(ostream& out) const
428 {
429 out << "[${{self.c_ident}}: ";
430 ''')
431
432 # For each field
433 code.indent()
434 for dm in self.data_members.values():
435 code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
436
437 if self.isMessage:
438 code('out << "Time = " << getTime() << " ";')
439 code.dedent()
440
441 # Trailer
442 code('''
443 out << "]";
444 }''')
445
446 code.write(path, "%s.cc" % self.c_ident)
447
448 def printEnumHH(self, path):
449 code = self.symtab.codeFormatter()
450 code('''
451 /** \\file ${{self.c_ident}}.hh
452 *
453 * Auto generated C++ code started by $__file__:$__line__
454 */
455
456 #ifndef __${{self.c_ident}}_HH__
457 #define __${{self.c_ident}}_HH__
458
459 #include <iostream>
460 #include <string>
461
462 #include "mem/ruby/common/Global.hh"
463
464 // Class definition
465 /** \\enum ${{self.c_ident}}
466 * \\brief ${{self.desc}}
467 */
468 enum ${{self.c_ident}} {
469 ${{self.c_ident}}_FIRST,
470 ''')
471
472 code.indent()
473 # For each field
474 for i,(ident,enum) in enumerate(self.enums.iteritems()):
475 desc = enum.get("desc", "No description avaliable")
476 if i == 0:
477 init = ' = %s_FIRST' % self.c_ident
478 else:
479 init = ''
480 code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
481 code.dedent()
482 code('''
483 ${{self.c_ident}}_NUM
484 };
485
486 // Code to convert from a string to the enumeration
487 ${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str);
488
489 // Code to convert state to a string
490 std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
491
492 // Code to increment an enumeration type
493 ${{self.c_ident}} &operator++(${{self.c_ident}} &e);
494 ''')
495
496 # MachineType hack used to set the base component id for each Machine
497 if self.isMachineType:
498 code('''
499 int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
500 MachineType ${{self.c_ident}}_from_base_level(int);
501 int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
502 int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
503 ''')
504
505 for enum in self.enums.itervalues():
506 code('#define MACHINETYPE_${{enum.ident}} 1')
507
508 # Trailer
509 code('''
510 std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
511
512 #endif // __${{self.c_ident}}_HH__
513 ''')
514
515 code.write(path, "%s.hh" % self.c_ident)
516
517 def printEnumCC(self, path):
518 code = self.symtab.codeFormatter()
519 code('''
520 /** \\file ${{self.c_ident}}.hh
521 *
522 * Auto generated C++ code started by $__file__:$__line__
523 */
524
525 #include <iostream>
526 #include <string>
527
528 #include "mem/protocol/${{self.c_ident}}.hh"
529
530 using namespace std;
531
532 ''')
533
534 if self.isMachineType:
535 for enum in self.enums.itervalues():
536 code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
537
538 code('''
539 // Code for output operator
540 ostream&
541 operator<<(ostream& out, const ${{self.c_ident}}& obj)
542 {
543 out << ${{self.c_ident}}_to_string(obj);
544 out << flush;
545 return out;
546 }
547
548 // Code to convert state to a string
549 string
550 ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
551 {
552 switch(obj) {
553 ''')
554
555 # For each field
556 code.indent()
557 for enum in self.enums.itervalues():
558 code(' case ${{self.c_ident}}_${{enum.ident}}:')
559 code(' return "${{enum.ident}}";')
560 code.dedent()
561
562 # Trailer
563 code('''
564 default:
565 ERROR_MSG("Invalid range for type ${{self.c_ident}}");
566 return "";
567 }
568 }
569
570 // Code to convert from a string to the enumeration
571 ${{self.c_ident}}
572 string_to_${{self.c_ident}}(const string& str)
573 {
574 ''')
575
576 # For each field
577 start = ""
578 code.indent()
579 for enum in self.enums.itervalues():
580 code('${start}if (str == "${{enum.ident}}") {')
581 code(' return ${{self.c_ident}}_${{enum.ident}};')
582 start = "} else "
583 code.dedent()
584
585 code('''
586 } else {
587 WARN_EXPR(str);
588 ERROR_MSG("Invalid string conversion for type ${{self.c_ident}}");
589 }
590 }
591
592 // Code to increment an enumeration type
593 ${{self.c_ident}}&
594 operator++(${{self.c_ident}}& e)
595 {
596 assert(e < ${{self.c_ident}}_NUM);
597 return e = ${{self.c_ident}}(e+1);
598 }
599 ''')
600
601 # MachineType hack used to set the base level and number of
602 # components for each Machine
603 if self.isMachineType:
604 code('''
605 /** \\brief returns the base vector index for each machine type to be
606 * used by NetDest
607 *
608 * \\return the base vector index for each machine type to be used by NetDest
609 * \\see NetDest.hh
610 */
611 int
612 ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
613 {
614 switch(obj) {
615 ''')
616
617 # For each field
618 code.indent()
619 for i,enum in enumerate(self.enums.itervalues()):
620 code(' case ${{self.c_ident}}_${{enum.ident}}:')
621 code(' return $i;')
622 code.dedent()
623
624 # total num
625 code('''
626 case ${{self.c_ident}}_NUM:
627 return ${{len(self.enums)}};
628
629 default:
630 ERROR_MSG("Invalid range for type ${{self.c_ident}}");
631 return -1;
632 }
633 }
634
635 /** \\brief returns the machine type for each base vector index used by NetDest
636 *
637 * \\return the MachineType
638 */
639 MachineType
640 ${{self.c_ident}}_from_base_level(int type)
641 {
642 switch(type) {
643 ''')
644
645 # For each field
646 code.indent()
647 for i,enum in enumerate(self.enums.itervalues()):
648 code(' case $i:')
649 code(' return ${{self.c_ident}}_${{enum.ident}};')
650 code.dedent()
651
652 # Trailer
653 code('''
654 default:
655 ERROR_MSG("Invalid range for type ${{self.c_ident}}");
656 return MachineType_NUM;
657 }
658 }
659
660 /** \\brief The return value indicates the number of components created
661 * before a particular machine\'s components
662 *
663 * \\return the base number of components for each machine
664 */
665 int
666 ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
667 {
668 int base = 0;
669 switch(obj) {
670 ''')
671
672 # For each field
673 code.indent()
674 code(' case ${{self.c_ident}}_NUM:')
675 for enum in reversed(self.enums.values()):
676 code(' base += ${{enum.ident}}_Controller::getNumControllers();')
677 code(' case ${{self.c_ident}}_${{enum.ident}}:')
678 code(' break;')
679 code.dedent()
680
681 code('''
682 default:
683 ERROR_MSG("Invalid range for type ${{self.c_ident}}");
684 return -1;
685 }
686
687 return base;
688 }
689
690 /** \\brief returns the total number of components for each machine
691 * \\return the total number of components for each machine
692 */
693 int
694 ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
695 {
696 switch(obj) {
697 ''')
698
699 # For each field
700 for enum in self.enums.itervalues():
701 code('''
702 case ${{self.c_ident}}_${{enum.ident}}:
703 return ${{enum.ident}}_Controller::getNumControllers();
704 ''')
705
706 # total num
707 code('''
708 case ${{self.c_ident}}_NUM:
709 default:
710 ERROR_MSG("Invalid range for type ${{self.c_ident}}");
711 return -1;
712 }
713 }
714 ''')
715
716 # Write the file
717 code.write(path, "%s.cc" % self.c_ident)
718
719 __all__ = [ "Type" ]