Type.py revision 9631:5ebde5544529
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
32from slicc.symbols.Var import Var
33
34class DataMember(PairContainer):
35    def __init__(self, ident, type, pairs, init_code):
36        super(DataMember, self).__init__(pairs)
37        self.ident = ident
38        self.type = type
39        self.init_code = init_code
40
41class Enumeration(PairContainer):
42    def __init__(self, ident, pairs):
43        super(Enumeration, self).__init__(pairs)
44        self.ident = ident
45
46class Method(object):
47    def __init__(self, return_type, param_types):
48        self.return_type = return_type
49        self.param_types = param_types
50
51class Type(Symbol):
52    def __init__(self, table, ident, location, pairs, machine=None):
53        super(Type, self).__init__(table, ident, location, pairs)
54        self.c_ident = ident
55        self.abstract_ident = ""
56        if machine:
57            if self.isExternal or self.isPrimitive:
58                if "external_name" in self:
59                    self.c_ident = self["external_name"]
60            else:
61                # Append with machine name
62                self.c_ident = "%s_%s" % (machine, ident)
63
64        self.pairs.setdefault("desc", "No description avaliable")
65
66        # check for interface that this Type implements
67        if "interface" in self:
68            interface = self["interface"]
69            if interface in ("Message", "NetworkMessage"):
70                self["message"] = "yes"
71            if interface == "NetworkMessage":
72                self["networkmessage"] = "yes"
73
74        # FIXME - all of the following id comparisons are fragile hacks
75        if self.ident in ("CacheMemory", "NewCacheMemory",
76                          "TLCCacheMemory", "DNUCACacheMemory",
77                          "DNUCABankCacheMemory", "L2BankCacheMemory",
78                          "CompressedCacheMemory", "PrefetchCacheMemory"):
79            self["cache"] = "yes"
80
81        if self.ident in ("TBETable", "DNUCATBETable", "DNUCAStopTable"):
82            self["tbe"] = "yes"
83
84        if self.ident == "NewTBETable":
85            self["newtbe"] = "yes"
86
87        if self.ident == "TimerTable":
88            self["timer"] = "yes"
89
90        if self.ident == "DirectoryMemory":
91            self["dir"] = "yes"
92
93        if self.ident == "PersistentTable":
94            self["persistent"] = "yes"
95
96        if self.ident == "Prefetcher":
97            self["prefetcher"] = "yes"
98
99        if self.ident == "DNUCA_Movement":
100            self["mover"] = "yes"
101
102        self.isMachineType = (ident == "MachineType")
103
104        self.isStateDecl = ("state_decl" in self)
105        self.statePermPairs = []
106
107        self.data_members = orderdict()
108
109        # Methods
110        self.methods = {}
111        self.functions = {}
112
113        # Enums
114        self.enums = orderdict()
115
116    @property
117    def isPrimitive(self):
118        return "primitive" in self
119    @property
120    def isNetworkMessage(self):
121        return "networkmessage" in self
122    @property
123    def isMessage(self):
124        return "message" in self
125    @property
126    def isBuffer(self):
127        return "buffer" in self
128    @property
129    def isInPort(self):
130        return "inport" in self
131    @property
132    def isOutPort(self):
133        return "outport" in self
134    @property
135    def isEnumeration(self):
136        return "enumeration" in self
137    @property
138    def isExternal(self):
139        return "external" in self
140    @property
141    def isGlobal(self):
142        return "global" in self
143    @property
144    def isInterface(self):
145        return "interface" in self
146
147    # Return false on error
148    def addDataMember(self, ident, type, pairs, init_code):
149        if ident in self.data_members:
150            return False
151
152        member = DataMember(ident, type, pairs, init_code)
153        self.data_members[ident] = member
154
155        var = Var(self.symtab, ident, self.location, type,
156                "m_%s" % ident, {}, None)
157        self.symtab.registerSym(ident, var)
158        return True
159
160    def dataMemberType(self, ident):
161        return self.data_members[ident].type
162
163    def methodId(self, name, param_type_vec):
164        return '_'.join([name] + [ pt.c_ident for pt in param_type_vec ])
165
166    def methodIdAbstract(self, name, param_type_vec):
167        return '_'.join([name] + [ pt.abstract_ident for pt in param_type_vec ])
168
169    def statePermPairAdd(self, state_name, perm_name):
170        self.statePermPairs.append([state_name, perm_name])
171
172    def addMethod(self, name, return_type, param_type_vec):
173        ident = self.methodId(name, param_type_vec)
174        if ident in self.methods:
175            return False
176
177        self.methods[ident] = Method(return_type, param_type_vec)
178        return True
179
180    # Ideally either this function or the one above should exist. But
181    # methods and functions have different structures right now.
182    # Hence, these are different, at least for the time being.
183    def addFunc(self, func):
184        ident = self.methodId(func.ident, func.param_types)
185        if ident in self.functions:
186            return False
187
188        self.functions[ident] = func
189        return True
190
191    def addEnum(self, ident, pairs):
192        if ident in self.enums:
193            return False
194
195        self.enums[ident] = Enumeration(ident, pairs)
196
197        # Add default
198        if "default" not in self:
199            self["default"] = "%s_NUM" % self.c_ident
200
201        return True
202
203    def writeCodeFiles(self, path, includes):
204        if self.isExternal:
205            # Do nothing
206            pass
207        elif self.isEnumeration:
208            self.printEnumHH(path)
209            self.printEnumCC(path)
210        else:
211            # User defined structs and messages
212            self.printTypeHH(path)
213            self.printTypeCC(path)
214
215    def printTypeHH(self, path):
216        code = self.symtab.codeFormatter()
217        code('''
218/** \\file ${{self.c_ident}}.hh
219 *
220 *
221 * Auto generated C++ code started by $__file__:$__line__
222 */
223
224#ifndef __${{self.c_ident}}_HH__
225#define __${{self.c_ident}}_HH__
226
227#include <iostream>
228
229#include "mem/ruby/slicc_interface/RubySlicc_Util.hh"
230''')
231
232        for dm in self.data_members.values():
233            if not dm.type.isPrimitive:
234                code('#include "mem/protocol/$0.hh"', dm.type.c_ident)
235
236        parent = ""
237        if "interface" in self:
238            code('#include "mem/protocol/$0.hh"', self["interface"])
239            parent = " :  public %s" % self["interface"]
240
241        code('''
242$klass ${{self.c_ident}}$parent
243{
244  public:
245    ${{self.c_ident}}
246''', klass="class")
247
248        if self.isMessage:
249            code('(Tick curTime) : %s(curTime) {' % self["interface"])
250        else:
251            code('()\n\t\t{')
252
253        code.indent()
254        if not self.isGlobal:
255            code.indent()
256            for dm in self.data_members.values():
257                ident = dm.ident
258                if "default" in dm:
259                    # look for default value
260                    code('m_$ident = ${{dm["default"]}}; // default for this field')
261                elif "default" in dm.type:
262                    # Look for the type default
263                    tid = dm.type.c_ident
264                    code('m_$ident = ${{dm.type["default"]}}; // default value of $tid')
265                else:
266                    code('// m_$ident has no default')
267            code.dedent()
268        code('}')
269
270        # ******** Copy constructor ********
271        if not self.isGlobal:
272            code('${{self.c_ident}}(const ${{self.c_ident}}&other)')
273
274            # Call superclass constructor
275            if "interface" in self:
276                code('    : ${{self["interface"]}}(other)')
277
278            code('{')
279            code.indent()
280
281            for dm in self.data_members.values():
282                code('m_${{dm.ident}} = other.m_${{dm.ident}};')
283
284            code.dedent()
285            code('}')
286
287        # ******** Full init constructor ********
288        if not self.isGlobal:
289            params = [ 'const %s& local_%s' % (dm.type.c_ident, dm.ident) \
290                       for dm in self.data_members.itervalues() ]
291            params = ', '.join(params)
292
293            if self.isMessage:
294                params = "const Tick curTime, " + params
295
296            code('${{self.c_ident}}($params)')
297
298            # Call superclass constructor
299            if "interface" in self:
300                if self.isMessage:
301                    code('    : ${{self["interface"]}}(curTime)')
302                else:
303                    code('    : ${{self["interface"]}}()')
304
305            code('{')
306            code.indent()
307            for dm in self.data_members.values():
308                code('m_${{dm.ident}} = local_${{dm.ident}};')
309                if "nextLineCallHack" in dm:
310                    code('m_${{dm.ident}}${{dm["nextLineCallHack"]}};')
311
312            code.dedent()
313            code('}')
314
315        # create a clone member
316        code('''
317${{self.c_ident}}*
318clone() const
319{
320     return new ${{self.c_ident}}(*this);
321}
322''')
323
324        if not self.isGlobal:
325            # const Get methods for each field
326            code('// Const accessors methods for each field')
327            for dm in self.data_members.values():
328                code('''
329/** \\brief Const accessor method for ${{dm.ident}} field.
330 *  \\return ${{dm.ident}} field
331 */
332const ${{dm.type.c_ident}}&
333get${{dm.ident}}() const
334{
335    return m_${{dm.ident}};
336}
337''')
338
339            # Non-const Get methods for each field
340            code('// Non const Accessors methods for each field')
341            for dm in self.data_members.values():
342                code('''
343/** \\brief Non-const accessor method for ${{dm.ident}} field.
344 *  \\return ${{dm.ident}} field
345 */
346${{dm.type.c_ident}}&
347get${{dm.ident}}()
348{
349    return m_${{dm.ident}};
350}
351''')
352
353            #Set methods for each field
354            code('// Mutator methods for each field')
355            for dm in self.data_members.values():
356                code('''
357/** \\brief Mutator method for ${{dm.ident}} field */
358void
359set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}})
360{
361    m_${{dm.ident}} = local_${{dm.ident}};
362}
363''')
364
365        code('void print(std::ostream& out) const;')
366        code.dedent()
367        code('  //private:')
368        code.indent()
369
370        # Data members for each field
371        for dm in self.data_members.values():
372            if "abstract" not in dm:
373                const = ""
374                init = ""
375
376                # global structure
377                if self.isGlobal:
378                    const = "static const "
379
380                # init value
381                if dm.init_code:
382                    # only global structure can have init value here
383                    assert self.isGlobal
384                    init = " = %s" % (dm.init_code)
385
386                if "desc" in dm:
387                    code('/** ${{dm["desc"]}} */')
388
389                code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init;')
390
391        # Prototypes for functions defined for the Type
392        for item in self.functions:
393            proto = self.functions[item].prototype
394            if proto:
395                code('$proto')
396
397        code.dedent()
398        code('};')
399
400        code('''
401inline std::ostream&
402operator<<(std::ostream& out, const ${{self.c_ident}}& obj)
403{
404    obj.print(out);
405    out << std::flush;
406    return out;
407}
408
409#endif // __${{self.c_ident}}_HH__
410''')
411
412        code.write(path, "%s.hh" % self.c_ident)
413
414    def printTypeCC(self, path):
415        code = self.symtab.codeFormatter()
416
417        code('''
418/** \\file ${{self.c_ident}}.cc
419 *
420 * Auto generated C++ code started by $__file__:$__line__
421 */
422
423#include <iostream>
424
425#include "mem/protocol/${{self.c_ident}}.hh"
426#include "mem/ruby/common/Global.hh"
427#include "mem/ruby/system/System.hh"
428
429using namespace std;
430''')
431
432        code('''
433/** \\brief Print the state of this object */
434void
435${{self.c_ident}}::print(ostream& out) const
436{
437    out << "[${{self.c_ident}}: ";
438''')
439
440        # For each field
441        code.indent()
442        for dm in self.data_members.values():
443            code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
444
445        if self.isMessage:
446            code('out << "Time = " << g_system_ptr->clockPeriod() * getTime() << " ";')
447        code.dedent()
448
449        # Trailer
450        code('''
451    out << "]";
452}''')
453
454        # print the code for the functions in the type
455        for item in self.functions:
456            code(self.functions[item].generateCode())
457
458        code.write(path, "%s.cc" % self.c_ident)
459
460    def printEnumHH(self, path):
461        code = self.symtab.codeFormatter()
462        code('''
463/** \\file ${{self.c_ident}}.hh
464 *
465 * Auto generated C++ code started by $__file__:$__line__
466 */
467
468#ifndef __${{self.c_ident}}_HH__
469#define __${{self.c_ident}}_HH__
470
471#include <iostream>
472#include <string>
473
474''')
475        if self.isStateDecl:
476            code('#include "mem/protocol/AccessPermission.hh"')
477
478        if self.isMachineType:
479            code('#include "base/misc.hh"')
480            code('#include "mem/protocol/GenericMachineType.hh"')
481            code('#include "mem/ruby/common/Address.hh"')
482            code('struct MachineID;')
483
484        code('''
485
486// Class definition
487/** \\enum ${{self.c_ident}}
488 *  \\brief ${{self.desc}}
489 */
490enum ${{self.c_ident}} {
491    ${{self.c_ident}}_FIRST,
492''')
493
494        code.indent()
495        # For each field
496        for i,(ident,enum) in enumerate(self.enums.iteritems()):
497            desc = enum.get("desc", "No description avaliable")
498            if i == 0:
499                init = ' = %s_FIRST' % self.c_ident
500            else:
501                init = ''
502            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
503        code.dedent()
504        code('''
505    ${{self.c_ident}}_NUM
506};
507
508// Code to convert from a string to the enumeration
509${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str);
510
511// Code to convert state to a string
512std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
513
514// Code to increment an enumeration type
515${{self.c_ident}} &operator++(${{self.c_ident}} &e);
516''')
517
518        # MachineType hack used to set the base component id for each Machine
519        if self.isMachineType:
520            code('''
521int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
522MachineType ${{self.c_ident}}_from_base_level(int);
523int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
524int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
525''')
526
527            for enum in self.enums.itervalues():
528                if enum.ident == "DMA":
529                    code('''
530MachineID map_Address_to_DMA(const Address &addr);
531''')
532                code('''
533
534MachineID get${{enum.ident}}MachineID(NodeID RubyNode);
535''')
536
537            code('''
538inline GenericMachineType
539ConvertMachToGenericMach(MachineType machType)
540{
541''')
542            for enum in self.enums.itervalues():
543                genericType = self.enums[enum.ident].get('genericType',
544                                                         enum.ident)
545                code('''
546      if (machType == MachineType_${{enum.ident}})
547          return GenericMachineType_${{genericType}};
548''')
549            code('''
550      panic("cannot convert to a GenericMachineType");
551}
552''')
553
554        if self.isStateDecl:
555            code('''
556
557// Code to convert the current state to an access permission
558AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj);
559
560''')
561
562        # Trailer
563        code('''
564std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
565
566#endif // __${{self.c_ident}}_HH__
567''')
568
569        code.write(path, "%s.hh" % self.c_ident)
570
571    def printEnumCC(self, path):
572        code = self.symtab.codeFormatter()
573        code('''
574/** \\file ${{self.c_ident}}.hh
575 *
576 * Auto generated C++ code started by $__file__:$__line__
577 */
578
579#include <cassert>
580#include <iostream>
581#include <string>
582
583#include "base/misc.hh"
584#include "mem/protocol/${{self.c_ident}}.hh"
585
586using namespace std;
587
588''')
589
590        if self.isStateDecl:
591            code('''
592// Code to convert the current state to an access permission
593AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj)
594{
595    switch(obj) {
596''')
597            # For each case
598            code.indent()
599            for statePerm in self.statePermPairs:
600                code('  case ${{self.c_ident}}_${{statePerm[0]}}:')
601                code('    return AccessPermission_${{statePerm[1]}};')
602            code.dedent()
603            code ('''
604      default:
605        panic("Unknown state access permission converstion for ${{self.c_ident}}");
606    }
607}
608
609''')
610
611        if self.isMachineType:
612            for enum in self.enums.itervalues():
613                code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
614            code('#include "mem/ruby/system/MachineID.hh"')
615
616        code('''
617// Code for output operator
618ostream&
619operator<<(ostream& out, const ${{self.c_ident}}& obj)
620{
621    out << ${{self.c_ident}}_to_string(obj);
622    out << flush;
623    return out;
624}
625
626// Code to convert state to a string
627string
628${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
629{
630    switch(obj) {
631''')
632
633        # For each field
634        code.indent()
635        for enum in self.enums.itervalues():
636            code('  case ${{self.c_ident}}_${{enum.ident}}:')
637            code('    return "${{enum.ident}}";')
638        code.dedent()
639
640        # Trailer
641        code('''
642      default:
643        panic("Invalid range for type ${{self.c_ident}}");
644    }
645}
646
647// Code to convert from a string to the enumeration
648${{self.c_ident}}
649string_to_${{self.c_ident}}(const string& str)
650{
651''')
652
653        # For each field
654        start = ""
655        code.indent()
656        for enum in self.enums.itervalues():
657            code('${start}if (str == "${{enum.ident}}") {')
658            code('    return ${{self.c_ident}}_${{enum.ident}};')
659            start = "} else "
660        code.dedent()
661
662        code('''
663    } else {
664        panic("Invalid string conversion for %s, type ${{self.c_ident}}", str);
665    }
666}
667
668// Code to increment an enumeration type
669${{self.c_ident}}&
670operator++(${{self.c_ident}}& e)
671{
672    assert(e < ${{self.c_ident}}_NUM);
673    return e = ${{self.c_ident}}(e+1);
674}
675''')
676
677        # MachineType hack used to set the base level and number of
678        # components for each Machine
679        if self.isMachineType:
680            code('''
681/** \\brief returns the base vector index for each machine type to be
682  * used by NetDest
683  *
684  * \\return the base vector index for each machine type to be used by NetDest
685  * \\see NetDest.hh
686  */
687int
688${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
689{
690    switch(obj) {
691''')
692
693            # For each field
694            code.indent()
695            for i,enum in enumerate(self.enums.itervalues()):
696                code('  case ${{self.c_ident}}_${{enum.ident}}:')
697                code('    return $i;')
698            code.dedent()
699
700            # total num
701            code('''
702      case ${{self.c_ident}}_NUM:
703        return ${{len(self.enums)}};
704
705      default:
706        panic("Invalid range for type ${{self.c_ident}}");
707    }
708}
709
710/** \\brief returns the machine type for each base vector index used by NetDest
711 *
712 * \\return the MachineType
713 */
714MachineType
715${{self.c_ident}}_from_base_level(int type)
716{
717    switch(type) {
718''')
719
720            # For each field
721            code.indent()
722            for i,enum in enumerate(self.enums.itervalues()):
723                code('  case $i:')
724                code('    return ${{self.c_ident}}_${{enum.ident}};')
725            code.dedent()
726
727            # Trailer
728            code('''
729      default:
730        panic("Invalid range for type ${{self.c_ident}}");
731    }
732}
733
734/** \\brief The return value indicates the number of components created
735 * before a particular machine\'s components
736 *
737 * \\return the base number of components for each machine
738 */
739int
740${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
741{
742    int base = 0;
743    switch(obj) {
744''')
745
746            # For each field
747            code.indent()
748            code('  case ${{self.c_ident}}_NUM:')
749            for enum in reversed(self.enums.values()):
750                code('    base += ${{enum.ident}}_Controller::getNumControllers();')
751                code('  case ${{self.c_ident}}_${{enum.ident}}:')
752            code('    break;')
753            code.dedent()
754
755            code('''
756      default:
757        panic("Invalid range for type ${{self.c_ident}}");
758    }
759
760    return base;
761}
762
763/** \\brief returns the total number of components for each machine
764 * \\return the total number of components for each machine
765 */
766int
767${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
768{
769    switch(obj) {
770''')
771
772            # For each field
773            for enum in self.enums.itervalues():
774                code('''
775      case ${{self.c_ident}}_${{enum.ident}}:
776        return ${{enum.ident}}_Controller::getNumControllers();
777''')
778
779            # total num
780            code('''
781      case ${{self.c_ident}}_NUM:
782      default:
783        panic("Invalid range for type ${{self.c_ident}}");
784    }
785}
786''')
787
788            for enum in self.enums.itervalues():
789                if enum.ident == "DMA":
790                    code('''
791MachineID
792map_Address_to_DMA(const Address &addr)
793{
794      MachineID dma = {MachineType_DMA, 0};
795      return dma;
796}
797''')
798
799                code('''
800
801MachineID
802get${{enum.ident}}MachineID(NodeID RubyNode)
803{
804      MachineID mach = {MachineType_${{enum.ident}}, RubyNode};
805      return mach;
806}
807''')
808
809        # Write the file
810        code.write(path, "%s.cc" % self.c_ident)
811
812__all__ = [ "Type" ]
813