Type.py revision 12436:c56112090c61
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                code('''
527
528MachineID get${{enum.ident}}MachineID(NodeID RubyNode);
529''')
530
531        if self.isStateDecl:
532            code('''
533
534// Code to convert the current state to an access permission
535AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj);
536
537''')
538
539        # Trailer
540        code('''
541std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
542
543#endif // __${{self.c_ident}}_HH__
544''')
545
546        code.write(path, "%s.hh" % self.c_ident)
547
548    def printEnumCC(self, path):
549        code = self.symtab.codeFormatter()
550        code('''
551/** \\file ${{self.c_ident}}.hh
552 *
553 * Auto generated C++ code started by $__file__:$__line__
554 */
555
556#include <cassert>
557#include <iostream>
558#include <string>
559
560#include "base/logging.hh"
561#include "mem/protocol/${{self.c_ident}}.hh"
562
563using namespace std;
564
565''')
566
567        if self.isStateDecl:
568            code('''
569// Code to convert the current state to an access permission
570AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj)
571{
572    switch(obj) {
573''')
574            # For each case
575            code.indent()
576            for statePerm in self.statePermPairs:
577                code('  case ${{self.c_ident}}_${{statePerm[0]}}:')
578                code('    return AccessPermission_${{statePerm[1]}};')
579            code.dedent()
580            code ('''
581      default:
582        panic("Unknown state access permission converstion for ${{self.c_ident}}");
583    }
584}
585
586''')
587
588        if self.isMachineType:
589            for enum in self.enums.itervalues():
590                if enum.primary:
591                    code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
592            code('#include "mem/ruby/common/MachineID.hh"')
593
594        code('''
595// Code for output operator
596ostream&
597operator<<(ostream& out, const ${{self.c_ident}}& obj)
598{
599    out << ${{self.c_ident}}_to_string(obj);
600    out << flush;
601    return out;
602}
603
604// Code to convert state to a string
605string
606${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
607{
608    switch(obj) {
609''')
610
611        # For each field
612        code.indent()
613        for enum in self.enums.itervalues():
614            code('  case ${{self.c_ident}}_${{enum.ident}}:')
615            code('    return "${{enum.ident}}";')
616        code.dedent()
617
618        # Trailer
619        code('''
620      default:
621        panic("Invalid range for type ${{self.c_ident}}");
622    }
623}
624
625// Code to convert from a string to the enumeration
626${{self.c_ident}}
627string_to_${{self.c_ident}}(const string& str)
628{
629''')
630
631        # For each field
632        start = ""
633        code.indent()
634        for enum in self.enums.itervalues():
635            code('${start}if (str == "${{enum.ident}}") {')
636            code('    return ${{self.c_ident}}_${{enum.ident}};')
637            start = "} else "
638        code.dedent()
639
640        code('''
641    } else {
642        panic("Invalid string conversion for %s, type ${{self.c_ident}}", str);
643    }
644}
645
646// Code to increment an enumeration type
647${{self.c_ident}}&
648operator++(${{self.c_ident}}& e)
649{
650    assert(e < ${{self.c_ident}}_NUM);
651    return e = ${{self.c_ident}}(e+1);
652}
653''')
654
655        # MachineType hack used to set the base level and number of
656        # components for each Machine
657        if self.isMachineType:
658            code('''
659/** \\brief returns the base vector index for each machine type to be
660  * used by NetDest
661  *
662  * \\return the base vector index for each machine type to be used by NetDest
663  * \\see NetDest.hh
664  */
665int
666${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
667{
668    switch(obj) {
669''')
670
671            # For each field
672            code.indent()
673            for i,enum in enumerate(self.enums.itervalues()):
674                code('  case ${{self.c_ident}}_${{enum.ident}}:')
675                code('    return $i;')
676            code.dedent()
677
678            # total num
679            code('''
680      case ${{self.c_ident}}_NUM:
681        return ${{len(self.enums)}};
682
683      default:
684        panic("Invalid range for type ${{self.c_ident}}");
685    }
686}
687
688/** \\brief returns the machine type for each base vector index used by NetDest
689 *
690 * \\return the MachineType
691 */
692MachineType
693${{self.c_ident}}_from_base_level(int type)
694{
695    switch(type) {
696''')
697
698            # For each field
699            code.indent()
700            for i,enum in enumerate(self.enums.itervalues()):
701                code('  case $i:')
702                code('    return ${{self.c_ident}}_${{enum.ident}};')
703            code.dedent()
704
705            # Trailer
706            code('''
707      default:
708        panic("Invalid range for type ${{self.c_ident}}");
709    }
710}
711
712/** \\brief The return value indicates the number of components created
713 * before a particular machine\'s components
714 *
715 * \\return the base number of components for each machine
716 */
717int
718${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
719{
720    int base = 0;
721    switch(obj) {
722''')
723
724            # For each field
725            code.indent()
726            code('  case ${{self.c_ident}}_NUM:')
727            for enum in reversed(self.enums.values()):
728                # Check if there is a defined machine with this type
729                if enum.primary:
730                    code('    base += ${{enum.ident}}_Controller::getNumControllers();')
731                else:
732                    code('    base += 0;')
733                code('    M5_FALLTHROUGH;')
734                code('  case ${{self.c_ident}}_${{enum.ident}}:')
735            code('    break;')
736            code.dedent()
737
738            code('''
739      default:
740        panic("Invalid range for type ${{self.c_ident}}");
741    }
742
743    return base;
744}
745
746/** \\brief returns the total number of components for each machine
747 * \\return the total number of components for each machine
748 */
749int
750${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
751{
752    switch(obj) {
753''')
754
755            # For each field
756            for enum in self.enums.itervalues():
757                code('case ${{self.c_ident}}_${{enum.ident}}:')
758                if enum.primary:
759                    code('return ${{enum.ident}}_Controller::getNumControllers();')
760                else:
761                    code('return 0;')
762
763            # total num
764            code('''
765      case ${{self.c_ident}}_NUM:
766      default:
767        panic("Invalid range for type ${{self.c_ident}}");
768    }
769}
770''')
771
772            for enum in self.enums.itervalues():
773                code('''
774
775MachineID
776get${{enum.ident}}MachineID(NodeID RubyNode)
777{
778      MachineID mach = {MachineType_${{enum.ident}}, RubyNode};
779      return mach;
780}
781''')
782
783        # Write the file
784        code.write(path, "%s.cc" % self.c_ident)
785
786__all__ = [ "Type" ]
787