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