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