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