Type.py revision 11117:2a1a21f79047
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            code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
416
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('#include "mem/ruby/common/TypeDefines.hh"')
452            code('struct MachineID;')
453
454        code('''
455
456// Class definition
457/** \\enum ${{self.c_ident}}
458 *  \\brief ${{self.desc}}
459 */
460enum ${{self.c_ident}} {
461    ${{self.c_ident}}_FIRST,
462''')
463
464        code.indent()
465        # For each field
466        for i,(ident,enum) in enumerate(self.enums.iteritems()):
467            desc = enum.get("desc", "No description avaliable")
468            if i == 0:
469                init = ' = %s_FIRST' % self.c_ident
470            else:
471                init = ''
472            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
473        code.dedent()
474        code('''
475    ${{self.c_ident}}_NUM
476};
477
478// Code to convert from a string to the enumeration
479${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str);
480
481// Code to convert state to a string
482std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
483
484// Code to increment an enumeration type
485${{self.c_ident}} &operator++(${{self.c_ident}} &e);
486''')
487
488        # MachineType hack used to set the base component id for each Machine
489        if self.isMachineType:
490            code('''
491int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
492MachineType ${{self.c_ident}}_from_base_level(int);
493int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
494int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
495''')
496
497            for enum in self.enums.itervalues():
498                if enum.ident == "DMA":
499                    code('''
500MachineID map_Address_to_DMA(const Addr &addr);
501''')
502                code('''
503
504MachineID get${{enum.ident}}MachineID(NodeID RubyNode);
505''')
506
507        if self.isStateDecl:
508            code('''
509
510// Code to convert the current state to an access permission
511AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj);
512
513''')
514
515        # Trailer
516        code('''
517std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
518
519#endif // __${{self.c_ident}}_HH__
520''')
521
522        code.write(path, "%s.hh" % self.c_ident)
523
524    def printEnumCC(self, path):
525        code = self.symtab.codeFormatter()
526        code('''
527/** \\file ${{self.c_ident}}.hh
528 *
529 * Auto generated C++ code started by $__file__:$__line__
530 */
531
532#include <cassert>
533#include <iostream>
534#include <string>
535
536#include "base/misc.hh"
537#include "mem/protocol/${{self.c_ident}}.hh"
538
539using namespace std;
540
541''')
542
543        if self.isStateDecl:
544            code('''
545// Code to convert the current state to an access permission
546AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj)
547{
548    switch(obj) {
549''')
550            # For each case
551            code.indent()
552            for statePerm in self.statePermPairs:
553                code('  case ${{self.c_ident}}_${{statePerm[0]}}:')
554                code('    return AccessPermission_${{statePerm[1]}};')
555            code.dedent()
556            code ('''
557      default:
558        panic("Unknown state access permission converstion for ${{self.c_ident}}");
559    }
560}
561
562''')
563
564        if self.isMachineType:
565            for enum in self.enums.itervalues():
566                if enum.get("Primary"):
567                    code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
568            code('#include "mem/ruby/common/MachineID.hh"')
569
570        code('''
571// Code for output operator
572ostream&
573operator<<(ostream& out, const ${{self.c_ident}}& obj)
574{
575    out << ${{self.c_ident}}_to_string(obj);
576    out << flush;
577    return out;
578}
579
580// Code to convert state to a string
581string
582${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
583{
584    switch(obj) {
585''')
586
587        # For each field
588        code.indent()
589        for enum in self.enums.itervalues():
590            code('  case ${{self.c_ident}}_${{enum.ident}}:')
591            code('    return "${{enum.ident}}";')
592        code.dedent()
593
594        # Trailer
595        code('''
596      default:
597        panic("Invalid range for type ${{self.c_ident}}");
598    }
599}
600
601// Code to convert from a string to the enumeration
602${{self.c_ident}}
603string_to_${{self.c_ident}}(const string& str)
604{
605''')
606
607        # For each field
608        start = ""
609        code.indent()
610        for enum in self.enums.itervalues():
611            code('${start}if (str == "${{enum.ident}}") {')
612            code('    return ${{self.c_ident}}_${{enum.ident}};')
613            start = "} else "
614        code.dedent()
615
616        code('''
617    } else {
618        panic("Invalid string conversion for %s, type ${{self.c_ident}}", str);
619    }
620}
621
622// Code to increment an enumeration type
623${{self.c_ident}}&
624operator++(${{self.c_ident}}& e)
625{
626    assert(e < ${{self.c_ident}}_NUM);
627    return e = ${{self.c_ident}}(e+1);
628}
629''')
630
631        # MachineType hack used to set the base level and number of
632        # components for each Machine
633        if self.isMachineType:
634            code('''
635/** \\brief returns the base vector index for each machine type to be
636  * used by NetDest
637  *
638  * \\return the base vector index for each machine type to be used by NetDest
639  * \\see NetDest.hh
640  */
641int
642${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
643{
644    switch(obj) {
645''')
646
647            # For each field
648            code.indent()
649            for i,enum in enumerate(self.enums.itervalues()):
650                code('  case ${{self.c_ident}}_${{enum.ident}}:')
651                code('    return $i;')
652            code.dedent()
653
654            # total num
655            code('''
656      case ${{self.c_ident}}_NUM:
657        return ${{len(self.enums)}};
658
659      default:
660        panic("Invalid range for type ${{self.c_ident}}");
661    }
662}
663
664/** \\brief returns the machine type for each base vector index used by NetDest
665 *
666 * \\return the MachineType
667 */
668MachineType
669${{self.c_ident}}_from_base_level(int type)
670{
671    switch(type) {
672''')
673
674            # For each field
675            code.indent()
676            for i,enum in enumerate(self.enums.itervalues()):
677                code('  case $i:')
678                code('    return ${{self.c_ident}}_${{enum.ident}};')
679            code.dedent()
680
681            # Trailer
682            code('''
683      default:
684        panic("Invalid range for type ${{self.c_ident}}");
685    }
686}
687
688/** \\brief The return value indicates the number of components created
689 * before a particular machine\'s components
690 *
691 * \\return the base number of components for each machine
692 */
693int
694${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
695{
696    int base = 0;
697    switch(obj) {
698''')
699
700            # For each field
701            code.indent()
702            code('  case ${{self.c_ident}}_NUM:')
703            for enum in reversed(self.enums.values()):
704                # Check if there is a defined machine with this type
705                if enum.get("Primary"):
706                    code('    base += ${{enum.ident}}_Controller::getNumControllers();')
707                else:
708                    code('    base += 0;')
709                code('  case ${{self.c_ident}}_${{enum.ident}}:')
710            code('    break;')
711            code.dedent()
712
713            code('''
714      default:
715        panic("Invalid range for type ${{self.c_ident}}");
716    }
717
718    return base;
719}
720
721/** \\brief returns the total number of components for each machine
722 * \\return the total number of components for each machine
723 */
724int
725${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
726{
727    switch(obj) {
728''')
729
730            # For each field
731            for enum in self.enums.itervalues():
732                code('case ${{self.c_ident}}_${{enum.ident}}:')
733                if enum.get("Primary"):
734                    code('return ${{enum.ident}}_Controller::getNumControllers();')
735                else:
736                    code('return 0;')
737
738            # total num
739            code('''
740      case ${{self.c_ident}}_NUM:
741      default:
742        panic("Invalid range for type ${{self.c_ident}}");
743    }
744}
745''')
746
747            for enum in self.enums.itervalues():
748                if enum.ident == "DMA":
749                    code('''
750MachineID
751map_Address_to_DMA(const Addr &addr)
752{
753      MachineID dma = {MachineType_DMA, 0};
754      return dma;
755}
756''')
757
758                code('''
759
760MachineID
761get${{enum.ident}}MachineID(NodeID RubyNode)
762{
763      MachineID mach = {MachineType_${{enum.ident}}, RubyNode};
764      return mach;
765}
766''')
767
768        # Write the file
769        code.write(path, "%s.cc" % self.c_ident)
770
771__all__ = [ "Type" ]
772