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