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