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