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