Type.py revision 8086
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
28from m5.util import orderdict
29
30from slicc.util import PairContainer
31from slicc.symbols.Symbol import Symbol
32
33class 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
40class Enumeration(PairContainer):
41    def __init__(self, ident, pairs):
42        super(Enumeration, self).__init__(pairs)
43        self.ident = ident
44
45class Method(object):
46    def __init__(self, return_type, param_types):
47        self.return_type = return_type
48        self.param_types = param_types
49
50class 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.isStateDecl = ("state_decl" in self)
104        self.statePermPairs = []
105
106        self.data_members = orderdict()
107
108        # Methods
109        self.methods = {}
110
111        # Enums
112        self.enums = orderdict()
113
114    @property
115    def isPrimitive(self):
116        return "primitive" in self
117    @property
118    def isNetworkMessage(self):
119        return "networkmessage" in self
120    @property
121    def isMessage(self):
122        return "message" in self
123    @property
124    def isBuffer(self):
125        return "buffer" in self
126    @property
127    def isInPort(self):
128        return "inport" in self
129    @property
130    def isOutPort(self):
131        return "outport" in self
132    @property
133    def isEnumeration(self):
134        return "enumeration" in self
135    @property
136    def isExternal(self):
137        return "external" in self
138    @property
139    def isGlobal(self):
140        return "global" in self
141    @property
142    def isInterface(self):
143        return "interface" in self
144
145    # Return false on error
146    def dataMemberAdd(self, ident, type, pairs, init_code):
147        if ident in self.data_members:
148            return False
149
150        member = DataMember(ident, type, pairs, init_code)
151        self.data_members[ident] = member
152
153        return True
154
155    def dataMemberType(self, ident):
156        return self.data_members[ident].type
157
158    def methodId(self, name, param_type_vec):
159        return '_'.join([name] + [ pt.c_ident for pt in param_type_vec ])
160
161    def methodIdAbstract(self, name, param_type_vec):
162        return '_'.join([name] + [ pt.abstract_ident for pt in param_type_vec ])
163
164    def statePermPairAdd(self, state_name, perm_name):
165        self.statePermPairs.append([state_name, perm_name])
166
167    def methodAdd(self, name, return_type, param_type_vec):
168        ident = self.methodId(name, param_type_vec)
169        if ident in self.methods:
170            return False
171
172        self.methods[ident] = Method(return_type, param_type_vec)
173        return True
174
175    def enumAdd(self, ident, pairs):
176        if ident in self.enums:
177            return False
178
179        self.enums[ident] = Enumeration(ident, pairs)
180
181        # Add default
182        if "default" not in self:
183            self["default"] = "%s_NUM" % self.c_ident
184
185        return True
186
187    def writeCodeFiles(self, path):
188        if self.isExternal:
189            # Do nothing
190            pass
191        elif self.isEnumeration:
192            self.printEnumHH(path)
193            self.printEnumCC(path)
194        else:
195            # User defined structs and messages
196            self.printTypeHH(path)
197            self.printTypeCC(path)
198
199    def printTypeHH(self, path):
200        code = self.symtab.codeFormatter()
201        code('''
202/** \\file ${{self.c_ident}}.hh
203 *
204 *
205 * Auto generated C++ code started by $__file__:$__line__
206 */
207
208#ifndef __${{self.c_ident}}_HH__
209#define __${{self.c_ident}}_HH__
210
211#include <iostream>
212
213#include "mem/ruby/common/Global.hh"
214''')
215
216        for dm in self.data_members.values():
217            if not dm.type.isPrimitive:
218                code('#include "mem/protocol/$0.hh"', dm.type.c_ident)
219
220        parent = ""
221        if "interface" in self:
222            code('#include "mem/protocol/$0.hh"', self["interface"])
223            parent = " :  public %s" % self["interface"]
224
225        code('''
226$klass ${{self.c_ident}}$parent
227{
228  public:
229    ${{self.c_ident}}()
230    {
231''', klass="class")
232
233        code.indent()
234        if not self.isGlobal:
235            code.indent()
236            for dm in self.data_members.values():
237                ident = dm.ident
238                if "default" in dm:
239                    # look for default value
240                    code('m_$ident = ${{dm["default"]}}; // default for this field')
241                elif "default" in dm.type:
242                    # Look for the type default
243                    tid = dm.type.c_ident
244                    code('m_$ident = ${{dm.type["default"]}}; // default value of $tid')
245                else:
246                    code('// m_$ident has no default')
247            code.dedent()
248        code('}')
249
250        # ******** Copy constructor ********
251        if not self.isGlobal:
252            code('${{self.c_ident}}(const ${{self.c_ident}}&other)')
253
254            # Call superclass constructor
255            if "interface" in self:
256                code('    : ${{self["interface"]}}(other)')
257
258            code('{')
259            code.indent()
260
261            for dm in self.data_members.values():
262                code('m_${{dm.ident}} = other.m_${{dm.ident}};')
263
264            if self.isMessage:
265                code('proc_id = other.proc_id;')
266
267            code.dedent()
268            code('}')
269
270        # ******** Full init constructor ********
271        if not self.isGlobal:
272            params = [ 'const %s& local_%s' % (dm.type.c_ident, dm.ident) \
273                       for dm in self.data_members.itervalues() ]
274
275            if self.isMessage:
276                params.append('const unsigned local_proc_id')
277
278            params = ', '.join(params)
279            code('${{self.c_ident}}($params)')
280
281            # Call superclass constructor
282            if "interface" in self:
283                code('    : ${{self["interface"]}}()')
284
285            code('{')
286            code.indent()
287            for dm in self.data_members.values():
288                code('m_${{dm.ident}} = local_${{dm.ident}};')
289                if "nextLineCallHack" in dm:
290                    code('m_${{dm.ident}}${{dm["nextLineCallHack"]}};')
291
292            if self.isMessage:
293                code('proc_id = local_proc_id;')
294
295            code.dedent()
296            code('}')
297
298        # create a static factory method and a clone member
299        code('''
300static ${{self.c_ident}}*
301create()
302{
303    return new ${{self.c_ident}}();
304}
305
306${{self.c_ident}}*
307clone() const
308{
309     return new ${{self.c_ident}}(*this);
310}
311''')
312
313        if not self.isGlobal:
314            # const Get methods for each field
315            code('// Const accessors methods for each field')
316            for dm in self.data_members.values():
317                code('''
318/** \\brief Const accessor method for ${{dm.ident}} field.
319 *  \\return ${{dm.ident}} field
320 */
321const ${{dm.type.c_ident}}&
322get${{dm.ident}}() const
323{
324    return m_${{dm.ident}};
325}
326''')
327
328            # Non-const Get methods for each field
329            code('// Non const Accessors methods for each field')
330            for dm in self.data_members.values():
331                code('''
332/** \\brief Non-const accessor method for ${{dm.ident}} field.
333 *  \\return ${{dm.ident}} field
334 */
335${{dm.type.c_ident}}&
336get${{dm.ident}}()
337{
338    return m_${{dm.ident}};
339}
340''')
341
342            #Set methods for each field
343            code('// Mutator methods for each field')
344            for dm in self.data_members.values():
345                code('''
346/** \\brief Mutator method for ${{dm.ident}} field */
347void
348set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}})
349{
350    m_${{dm.ident}} = local_${{dm.ident}};
351}
352''')
353
354        code('void print(std::ostream& out) const;')
355        code.dedent()
356        code('  //private:')
357        code.indent()
358
359        # Data members for each field
360        for dm in self.data_members.values():
361            if "abstract" not in dm:
362                const = ""
363                init = ""
364
365                # global structure
366                if self.isGlobal:
367                    const = "static const "
368
369                # init value
370                if dm.init_code:
371                    # only global structure can have init value here
372                    assert self.isGlobal
373                    init = " = %s" % (dm.init_code)
374
375                if "desc" in dm:
376                    code('/** ${{dm["desc"]}} */')
377
378                code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init;')
379
380        if self.isMessage:
381            code('unsigned proc_id;')
382
383        code.dedent()
384        code('};')
385
386        code('''
387inline std::ostream&
388operator<<(std::ostream& out, const ${{self.c_ident}}& obj)
389{
390    obj.print(out);
391    out << std::flush;
392    return out;
393}
394
395#endif // __${{self.c_ident}}_HH__
396''')
397
398        code.write(path, "%s.hh" % self.c_ident)
399
400    def printTypeCC(self, path):
401        code = self.symtab.codeFormatter()
402
403        code('''
404/** \\file ${{self.c_ident}}.cc
405 *
406 * Auto generated C++ code started by $__file__:$__line__
407 */
408
409#include <iostream>
410
411#include "mem/protocol/${{self.c_ident}}.hh"
412
413using namespace std;
414''')
415
416        code('''
417/** \\brief Print the state of this object */
418void
419${{self.c_ident}}::print(ostream& out) const
420{
421    out << "[${{self.c_ident}}: ";
422''')
423
424        # For each field
425        code.indent()
426        for dm in self.data_members.values():
427            code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
428
429        if self.isMessage:
430            code('out << "Time = " << getTime() << " ";')
431        code.dedent()
432
433        # Trailer
434        code('''
435    out << "]";
436}''')
437
438        code.write(path, "%s.cc" % self.c_ident)
439
440    def printEnumHH(self, path):
441        code = self.symtab.codeFormatter()
442        code('''
443/** \\file ${{self.c_ident}}.hh
444 *
445 * Auto generated C++ code started by $__file__:$__line__
446 */
447
448#ifndef __${{self.c_ident}}_HH__
449#define __${{self.c_ident}}_HH__
450
451#include <iostream>
452#include <string>
453
454#include "mem/ruby/common/Global.hh"
455''')
456        if self.isStateDecl:
457            code('#include "mem/protocol/AccessPermission.hh"')
458
459        code('''
460
461// Class definition
462/** \\enum ${{self.c_ident}}
463 *  \\brief ${{self.desc}}
464 */
465enum ${{self.c_ident}} {
466    ${{self.c_ident}}_FIRST,
467''')
468
469        code.indent()
470        # For each field
471        for i,(ident,enum) in enumerate(self.enums.iteritems()):
472            desc = enum.get("desc", "No description avaliable")
473            if i == 0:
474                init = ' = %s_FIRST' % self.c_ident
475            else:
476                init = ''
477            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
478        code.dedent()
479        code('''
480    ${{self.c_ident}}_NUM
481};
482
483// Code to convert from a string to the enumeration
484${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str);
485
486// Code to convert state to a string
487std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
488
489// Code to increment an enumeration type
490${{self.c_ident}} &operator++(${{self.c_ident}} &e);
491''')
492
493        # MachineType hack used to set the base component id for each Machine
494        if self.isMachineType:
495            code('''
496int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
497MachineType ${{self.c_ident}}_from_base_level(int);
498int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
499int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
500''')
501
502            for enum in self.enums.itervalues():
503                code('#define MACHINETYPE_${{enum.ident}} 1')
504
505        if self.isStateDecl:
506            code('''
507
508// Code to convert the current state to an access permission
509AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj);
510
511''')
512
513        # Trailer
514        code('''
515std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
516
517#endif // __${{self.c_ident}}_HH__
518''')
519
520        code.write(path, "%s.hh" % self.c_ident)
521
522    def printEnumCC(self, path):
523        code = self.symtab.codeFormatter()
524        code('''
525/** \\file ${{self.c_ident}}.hh
526 *
527 * Auto generated C++ code started by $__file__:$__line__
528 */
529
530#include <cassert>
531#include <iostream>
532#include <string>
533
534#include "base/misc.hh"
535#include "mem/protocol/${{self.c_ident}}.hh"
536
537using namespace std;
538
539''')
540
541        if self.isStateDecl:
542            code('''
543// Code to convert the current state to an access permission
544AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj)
545{
546    switch(obj) {
547''')
548            # For each case
549            code.indent()
550            for statePerm in self.statePermPairs:
551                code('  case ${{self.c_ident}}_${{statePerm[0]}}:')
552                code('    return AccessPermission_${{statePerm[1]}};')
553            code.dedent()
554            code ('''
555      default:
556        panic("Unknown state access permission converstion for ${{self.c_ident}}");
557    }
558}
559
560''')
561
562        if self.isMachineType:
563            for enum in self.enums.itervalues():
564                code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
565
566        code('''
567// Code for output operator
568ostream&
569operator<<(ostream& out, const ${{self.c_ident}}& obj)
570{
571    out << ${{self.c_ident}}_to_string(obj);
572    out << flush;
573    return out;
574}
575
576// Code to convert state to a string
577string
578${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
579{
580    switch(obj) {
581''')
582
583        # For each field
584        code.indent()
585        for enum in self.enums.itervalues():
586            code('  case ${{self.c_ident}}_${{enum.ident}}:')
587            code('    return "${{enum.ident}}";')
588        code.dedent()
589
590        # Trailer
591        code('''
592      default:
593        panic("Invalid range for type ${{self.c_ident}}");
594    }
595}
596
597// Code to convert from a string to the enumeration
598${{self.c_ident}}
599string_to_${{self.c_ident}}(const string& str)
600{
601''')
602
603        # For each field
604        start = ""
605        code.indent()
606        for enum in self.enums.itervalues():
607            code('${start}if (str == "${{enum.ident}}") {')
608            code('    return ${{self.c_ident}}_${{enum.ident}};')
609            start = "} else "
610        code.dedent()
611
612        code('''
613    } else {
614        panic("Invalid string conversion for %s, type ${{self.c_ident}}", str);
615    }
616}
617
618// Code to increment an enumeration type
619${{self.c_ident}}&
620operator++(${{self.c_ident}}& e)
621{
622    assert(e < ${{self.c_ident}}_NUM);
623    return e = ${{self.c_ident}}(e+1);
624}
625''')
626
627        # MachineType hack used to set the base level and number of
628        # components for each Machine
629        if self.isMachineType:
630            code('''
631/** \\brief returns the base vector index for each machine type to be
632  * used by NetDest
633  *
634  * \\return the base vector index for each machine type to be used by NetDest
635  * \\see NetDest.hh
636  */
637int
638${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
639{
640    switch(obj) {
641''')
642
643            # For each field
644            code.indent()
645            for i,enum in enumerate(self.enums.itervalues()):
646                code('  case ${{self.c_ident}}_${{enum.ident}}:')
647                code('    return $i;')
648            code.dedent()
649
650            # total num
651            code('''
652      case ${{self.c_ident}}_NUM:
653        return ${{len(self.enums)}};
654
655      default:
656        panic("Invalid range for type ${{self.c_ident}}");
657    }
658}
659
660/** \\brief returns the machine type for each base vector index used by NetDest
661 *
662 * \\return the MachineType
663 */
664MachineType
665${{self.c_ident}}_from_base_level(int type)
666{
667    switch(type) {
668''')
669
670            # For each field
671            code.indent()
672            for i,enum in enumerate(self.enums.itervalues()):
673                code('  case $i:')
674                code('    return ${{self.c_ident}}_${{enum.ident}};')
675            code.dedent()
676
677            # Trailer
678            code('''
679      default:
680        panic("Invalid range for type ${{self.c_ident}}");
681    }
682}
683
684/** \\brief The return value indicates the number of components created
685 * before a particular machine\'s components
686 *
687 * \\return the base number of components for each machine
688 */
689int
690${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
691{
692    int base = 0;
693    switch(obj) {
694''')
695
696            # For each field
697            code.indent()
698            code('  case ${{self.c_ident}}_NUM:')
699            for enum in reversed(self.enums.values()):
700                code('    base += ${{enum.ident}}_Controller::getNumControllers();')
701                code('  case ${{self.c_ident}}_${{enum.ident}}:')
702            code('    break;')
703            code.dedent()
704
705            code('''
706      default:
707        panic("Invalid range for type ${{self.c_ident}}");
708    }
709
710    return base;
711}
712
713/** \\brief returns the total number of components for each machine
714 * \\return the total number of components for each machine
715 */
716int
717${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
718{
719    switch(obj) {
720''')
721
722            # For each field
723            for enum in self.enums.itervalues():
724                code('''
725      case ${{self.c_ident}}_${{enum.ident}}:
726        return ${{enum.ident}}_Controller::getNumControllers();
727''')
728
729            # total num
730            code('''
731      case ${{self.c_ident}}_NUM:
732      default:
733        panic("Invalid range for type ${{self.c_ident}}");
734    }
735}
736''')
737
738        # Write the file
739        code.write(path, "%s.cc" % self.c_ident)
740
741__all__ = [ "Type" ]
742