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