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