Type.py revision 11089:4808f8c4a47e
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
276            code.dedent()
277            code('}')
278
279        # create a clone member
280        if self.isMessage:
281            code('''
282MsgPtr
283clone() const
284{
285     return std::shared_ptr<Message>(new ${{self.c_ident}}(*this));
286}
287''')
288        else:
289            code('''
290${{self.c_ident}}*
291clone() const
292{
293     return new ${{self.c_ident}}(*this);
294}
295''')
296
297        if not self.isGlobal:
298            # const Get methods for each field
299            code('// Const accessors methods for each field')
300            for dm in self.data_members.values():
301                code('''
302/** \\brief Const accessor method for ${{dm.ident}} field.
303 *  \\return ${{dm.ident}} field
304 */
305const ${{dm.type.c_ident}}&
306get${{dm.ident}}() const
307{
308    return m_${{dm.ident}};
309}
310''')
311
312            # Non-const Get methods for each field
313            code('// Non const Accessors methods for each field')
314            for dm in self.data_members.values():
315                code('''
316/** \\brief Non-const accessor method for ${{dm.ident}} field.
317 *  \\return ${{dm.ident}} field
318 */
319${{dm.type.c_ident}}&
320get${{dm.ident}}()
321{
322    return m_${{dm.ident}};
323}
324''')
325
326            #Set methods for each field
327            code('// Mutator methods for each field')
328            for dm in self.data_members.values():
329                code('''
330/** \\brief Mutator method for ${{dm.ident}} field */
331void
332set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}})
333{
334    m_${{dm.ident}} = local_${{dm.ident}};
335}
336''')
337
338        code('void print(std::ostream& out) const;')
339        code.dedent()
340        code('  //private:')
341        code.indent()
342
343        # Data members for each field
344        for dm in self.data_members.values():
345            if "abstract" not in dm:
346                const = ""
347                init = ""
348
349                # global structure
350                if self.isGlobal:
351                    const = "static const "
352
353                # init value
354                if dm.init_code:
355                    # only global structure can have init value here
356                    assert self.isGlobal
357                    init = " = %s" % (dm.init_code)
358
359                if "desc" in dm:
360                    code('/** ${{dm["desc"]}} */')
361
362                code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init;')
363
364        # Prototypes for methods defined for the Type
365        for item in self.methods:
366            proto = self.methods[item].prototype
367            if proto:
368                code('$proto')
369
370        code.dedent()
371        code('};')
372
373        code('''
374inline std::ostream&
375operator<<(std::ostream& out, const ${{self.c_ident}}& obj)
376{
377    obj.print(out);
378    out << std::flush;
379    return out;
380}
381
382#endif // __${{self.c_ident}}_HH__
383''')
384
385        code.write(path, "%s.hh" % self.c_ident)
386
387    def printTypeCC(self, path):
388        code = self.symtab.codeFormatter()
389
390        code('''
391/** \\file ${{self.c_ident}}.cc
392 *
393 * Auto generated C++ code started by $__file__:$__line__
394 */
395
396#include <iostream>
397#include <memory>
398
399#include "mem/protocol/${{self.c_ident}}.hh"
400#include "mem/ruby/system/System.hh"
401
402using namespace std;
403''')
404
405        code('''
406/** \\brief Print the state of this object */
407void
408${{self.c_ident}}::print(ostream& out) const
409{
410    out << "[${{self.c_ident}}: ";
411''')
412
413        # For each field
414        code.indent()
415        for dm in self.data_members.values():
416            code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
417
418        code.dedent()
419
420        # Trailer
421        code('''
422    out << "]";
423}''')
424
425        # print the code for the methods in the type
426        for item in self.methods:
427            code(self.methods[item].generateCode())
428
429        code.write(path, "%s.cc" % self.c_ident)
430
431    def printEnumHH(self, path):
432        code = self.symtab.codeFormatter()
433        code('''
434/** \\file ${{self.c_ident}}.hh
435 *
436 * Auto generated C++ code started by $__file__:$__line__
437 */
438
439#ifndef __${{self.c_ident}}_HH__
440#define __${{self.c_ident}}_HH__
441
442#include <iostream>
443#include <string>
444
445''')
446        if self.isStateDecl:
447            code('#include "mem/protocol/AccessPermission.hh"')
448
449        if self.isMachineType:
450            code('#include "base/misc.hh"')
451            code('#include "mem/ruby/common/Address.hh"')
452            code('#include "mem/ruby/common/TypeDefines.hh"')
453            code('struct MachineID;')
454
455        code('''
456
457// Class definition
458/** \\enum ${{self.c_ident}}
459 *  \\brief ${{self.desc}}
460 */
461enum ${{self.c_ident}} {
462    ${{self.c_ident}}_FIRST,
463''')
464
465        code.indent()
466        # For each field
467        for i,(ident,enum) in enumerate(self.enums.iteritems()):
468            desc = enum.get("desc", "No description avaliable")
469            if i == 0:
470                init = ' = %s_FIRST' % self.c_ident
471            else:
472                init = ''
473            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
474        code.dedent()
475        code('''
476    ${{self.c_ident}}_NUM
477};
478
479// Code to convert from a string to the enumeration
480${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str);
481
482// Code to convert state to a string
483std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
484
485// Code to increment an enumeration type
486${{self.c_ident}} &operator++(${{self.c_ident}} &e);
487''')
488
489        # MachineType hack used to set the base component id for each Machine
490        if self.isMachineType:
491            code('''
492int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
493MachineType ${{self.c_ident}}_from_base_level(int);
494int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
495int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
496''')
497
498            for enum in self.enums.itervalues():
499                if enum.ident == "DMA":
500                    code('''
501MachineID map_Address_to_DMA(const Addr &addr);
502''')
503                code('''
504
505MachineID get${{enum.ident}}MachineID(NodeID RubyNode);
506''')
507
508        if self.isStateDecl:
509            code('''
510
511// Code to convert the current state to an access permission
512AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj);
513
514''')
515
516        # Trailer
517        code('''
518std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
519
520#endif // __${{self.c_ident}}_HH__
521''')
522
523        code.write(path, "%s.hh" % self.c_ident)
524
525    def printEnumCC(self, path):
526        code = self.symtab.codeFormatter()
527        code('''
528/** \\file ${{self.c_ident}}.hh
529 *
530 * Auto generated C++ code started by $__file__:$__line__
531 */
532
533#include <cassert>
534#include <iostream>
535#include <string>
536
537#include "base/misc.hh"
538#include "mem/protocol/${{self.c_ident}}.hh"
539
540using namespace std;
541
542''')
543
544        if self.isStateDecl:
545            code('''
546// Code to convert the current state to an access permission
547AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj)
548{
549    switch(obj) {
550''')
551            # For each case
552            code.indent()
553            for statePerm in self.statePermPairs:
554                code('  case ${{self.c_ident}}_${{statePerm[0]}}:')
555                code('    return AccessPermission_${{statePerm[1]}};')
556            code.dedent()
557            code ('''
558      default:
559        panic("Unknown state access permission converstion for ${{self.c_ident}}");
560    }
561}
562
563''')
564
565        if self.isMachineType:
566            for enum in self.enums.itervalues():
567                if enum.get("Primary"):
568                    code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
569            code('#include "mem/ruby/common/MachineID.hh"')
570
571        code('''
572// Code for output operator
573ostream&
574operator<<(ostream& out, const ${{self.c_ident}}& obj)
575{
576    out << ${{self.c_ident}}_to_string(obj);
577    out << flush;
578    return out;
579}
580
581// Code to convert state to a string
582string
583${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
584{
585    switch(obj) {
586''')
587
588        # For each field
589        code.indent()
590        for enum in self.enums.itervalues():
591            code('  case ${{self.c_ident}}_${{enum.ident}}:')
592            code('    return "${{enum.ident}}";')
593        code.dedent()
594
595        # Trailer
596        code('''
597      default:
598        panic("Invalid range for type ${{self.c_ident}}");
599    }
600}
601
602// Code to convert from a string to the enumeration
603${{self.c_ident}}
604string_to_${{self.c_ident}}(const string& str)
605{
606''')
607
608        # For each field
609        start = ""
610        code.indent()
611        for enum in self.enums.itervalues():
612            code('${start}if (str == "${{enum.ident}}") {')
613            code('    return ${{self.c_ident}}_${{enum.ident}};')
614            start = "} else "
615        code.dedent()
616
617        code('''
618    } else {
619        panic("Invalid string conversion for %s, type ${{self.c_ident}}", str);
620    }
621}
622
623// Code to increment an enumeration type
624${{self.c_ident}}&
625operator++(${{self.c_ident}}& e)
626{
627    assert(e < ${{self.c_ident}}_NUM);
628    return e = ${{self.c_ident}}(e+1);
629}
630''')
631
632        # MachineType hack used to set the base level and number of
633        # components for each Machine
634        if self.isMachineType:
635            code('''
636/** \\brief returns the base vector index for each machine type to be
637  * used by NetDest
638  *
639  * \\return the base vector index for each machine type to be used by NetDest
640  * \\see NetDest.hh
641  */
642int
643${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
644{
645    switch(obj) {
646''')
647
648            # For each field
649            code.indent()
650            for i,enum in enumerate(self.enums.itervalues()):
651                code('  case ${{self.c_ident}}_${{enum.ident}}:')
652                code('    return $i;')
653            code.dedent()
654
655            # total num
656            code('''
657      case ${{self.c_ident}}_NUM:
658        return ${{len(self.enums)}};
659
660      default:
661        panic("Invalid range for type ${{self.c_ident}}");
662    }
663}
664
665/** \\brief returns the machine type for each base vector index used by NetDest
666 *
667 * \\return the MachineType
668 */
669MachineType
670${{self.c_ident}}_from_base_level(int type)
671{
672    switch(type) {
673''')
674
675            # For each field
676            code.indent()
677            for i,enum in enumerate(self.enums.itervalues()):
678                code('  case $i:')
679                code('    return ${{self.c_ident}}_${{enum.ident}};')
680            code.dedent()
681
682            # Trailer
683            code('''
684      default:
685        panic("Invalid range for type ${{self.c_ident}}");
686    }
687}
688
689/** \\brief The return value indicates the number of components created
690 * before a particular machine\'s components
691 *
692 * \\return the base number of components for each machine
693 */
694int
695${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
696{
697    int base = 0;
698    switch(obj) {
699''')
700
701            # For each field
702            code.indent()
703            code('  case ${{self.c_ident}}_NUM:')
704            for enum in reversed(self.enums.values()):
705                # Check if there is a defined machine with this type
706                if enum.get("Primary"):
707                    code('    base += ${{enum.ident}}_Controller::getNumControllers();')
708                else:
709                    code('    base += 0;')
710                code('  case ${{self.c_ident}}_${{enum.ident}}:')
711            code('    break;')
712            code.dedent()
713
714            code('''
715      default:
716        panic("Invalid range for type ${{self.c_ident}}");
717    }
718
719    return base;
720}
721
722/** \\brief returns the total number of components for each machine
723 * \\return the total number of components for each machine
724 */
725int
726${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
727{
728    switch(obj) {
729''')
730
731            # For each field
732            for enum in self.enums.itervalues():
733                code('case ${{self.c_ident}}_${{enum.ident}}:')
734                if enum.get("Primary"):
735                    code('return ${{enum.ident}}_Controller::getNumControllers();')
736                else:
737                    code('return 0;')
738
739            # total num
740            code('''
741      case ${{self.c_ident}}_NUM:
742      default:
743        panic("Invalid range for type ${{self.c_ident}}");
744    }
745}
746''')
747
748            for enum in self.enums.itervalues():
749                if enum.ident == "DMA":
750                    code('''
751MachineID
752map_Address_to_DMA(const Addr &addr)
753{
754      MachineID dma = {MachineType_DMA, 0};
755      return dma;
756}
757''')
758
759                code('''
760
761MachineID
762get${{enum.ident}}MachineID(NodeID RubyNode)
763{
764      MachineID mach = {MachineType_${{enum.ident}}, RubyNode};
765      return mach;
766}
767''')
768
769        # Write the file
770        code.write(path, "%s.cc" % self.c_ident)
771
772__all__ = [ "Type" ]
773