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