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