Type.py revision 7007:79413d1ec307
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.data_members = orderdict()
104
105        # Methods
106        self.methods = {}
107
108        # Enums
109        self.enums = orderdict()
110
111    @property
112    def isPrimitive(self):
113        return "primitive" in self
114    @property
115    def isNetworkMessage(self):
116        return "networkmessage" in self
117    @property
118    def isMessage(self):
119        return "message" in self
120    @property
121    def isBuffer(self):
122        return "buffer" in self
123    @property
124    def isInPort(self):
125        return "inport" in self
126    @property
127    def isOutPort(self):
128        return "outport" in self
129    @property
130    def isEnumeration(self):
131        return "enumeration" in self
132    @property
133    def isExternal(self):
134        return "external" in self
135    @property
136    def isGlobal(self):
137        return "global" in self
138    @property
139    def isInterface(self):
140        return "interface" in self
141
142    # Return false on error
143    def dataMemberAdd(self, ident, type, pairs, init_code):
144        if ident in self.data_members:
145            return False
146
147        member = DataMember(ident, type, pairs, init_code)
148        self.data_members[ident] = member
149
150        return True
151
152    def dataMemberType(self, ident):
153        return self.data_members[ident].type
154
155    def methodId(self, name, param_type_vec):
156        return '_'.join([name] + [ pt.c_ident for pt in param_type_vec ])
157
158    def methodIdAbstract(self, name, param_type_vec):
159        return '_'.join([name] + [ pt.abstract_ident for pt in param_type_vec ])
160
161    def methodAdd(self, name, return_type, param_type_vec):
162        ident = self.methodId(name, param_type_vec)
163        if ident in self.methods:
164            return False
165
166        self.methods[ident] = Method(return_type, param_type_vec)
167        return True
168
169    def enumAdd(self, ident, pairs):
170        if ident in self.enums:
171            return False
172
173        self.enums[ident] = Enumeration(ident, pairs)
174
175        # Add default
176        if "default" not in self:
177            self["default"] = "%s_NUM" % self.c_ident
178
179        return True
180
181    def writeCodeFiles(self, path):
182        if self.isExternal:
183            # Do nothing
184            pass
185        elif self.isEnumeration:
186            self.printEnumHH(path)
187            self.printEnumCC(path)
188        else:
189            # User defined structs and messages
190            self.printTypeHH(path)
191            self.printTypeCC(path)
192
193    def printTypeHH(self, path):
194        code = self.symtab.codeFormatter()
195        code('''
196/** \\file ${{self.c_ident}}.hh
197 *
198 *
199 * Auto generated C++ code started by $__file__:$__line__
200 */
201
202#ifndef __${{self.c_ident}}_HH__
203#define __${{self.c_ident}}_HH__
204
205#include <iostream>
206
207#include "mem/ruby/common/Global.hh"
208#include "mem/gems_common/Allocator.hh"
209''')
210
211        for dm in self.data_members.values():
212            if not dm.type.isPrimitive:
213                code('#include "mem/protocol/$0.hh"', dm.type.c_ident)
214
215        parent = ""
216        if "interface" in self:
217            code('#include "mem/protocol/$0.hh"', self["interface"])
218            parent = " :  public %s" % self["interface"]
219
220        code('''
221$klass ${{self.c_ident}}$parent
222{
223  public:
224    ${{self.c_ident}}()
225    {
226''', klass="class")
227
228        code.indent()
229        if not self.isGlobal:
230            code.indent()
231            for dm in self.data_members.values():
232                ident = dm.ident
233                if "default" in dm:
234                    # look for default value
235                    code('m_$ident = ${{dm["default"]}}; // default for this field')
236                elif "default" in dm.type:
237                    # Look for the type default
238                    tid = dm.type.c_ident
239                    code('m_$ident = ${{dm.type["default"]}}; // default value of $tid')
240                else:
241                    code('// m_$ident has no default')
242            code.dedent()
243        code('}')
244
245        # ******** Full init constructor ********
246        if not self.isGlobal:
247            params = [ 'const %s& local_%s' % (dm.type.c_ident, dm.ident) \
248                       for dm in self.data_members.itervalues() ]
249
250            if self.isMessage:
251                params.append('const unsigned local_proc_id')
252
253            params = ', '.join(params)
254            code('${{self.c_ident}}($params)')
255
256            # Call superclass constructor
257            if "interface" in self:
258                code('    : ${{self["interface"]}}()')
259
260            code('{')
261            code.indent()
262            for dm in self.data_members.values():
263                code('m_${{dm.ident}} = local_${{dm.ident}};')
264                if "nextLineCallHack" in dm:
265                    code('m_${{dm.ident}}${{dm["nextLineCallHack"]}};')
266
267            if self.isMessage:
268                code('proc_id = local_proc_id;')
269
270            code.dedent()
271            code('}')
272
273        # create a static factory method
274        if "interface" in self:
275            code('''
276static ${{self["interface"]}}*
277create()
278{
279    return new ${{self.c_ident}}();
280}
281''')
282
283        # ******** Message member functions ********
284        # FIXME: those should be moved into slicc file, slicc should
285        # support more of the c++ class inheritance
286
287        if self.isMessage:
288            code('''
289Message *
290clone() const
291{
292    checkAllocator();
293    return s_allocator_ptr->allocate(*this);
294}
295
296void
297destroy()
298{
299    checkAllocator();
300    s_allocator_ptr->deallocate(this);
301}
302
303static Allocator<${{self.c_ident}}>* s_allocator_ptr;
304
305static void
306checkAllocator()
307{
308    if (s_allocator_ptr == NULL) {
309        s_allocator_ptr = new Allocator<${{self.c_ident}}>;
310    }
311}
312''')
313
314        if not self.isGlobal:
315            # const Get methods for each field
316            code('// Const accessors methods for each field')
317            for dm in self.data_members.values():
318                code('''
319/** \\brief Const accessor method for ${{dm.ident}} field.
320 *  \\return ${{dm.ident}} field
321 */
322const ${{dm.type.c_ident}}&
323get${{dm.ident}}() const
324{
325    return m_${{dm.ident}};
326}
327''')
328
329            # Non-const Get methods for each field
330            code('// Non const Accessors methods for each field')
331            for dm in self.data_members.values():
332                code('''
333/** \\brief Non-const accessor method for ${{dm.ident}} field.
334 *  \\return ${{dm.ident}} field
335 */
336${{dm.type.c_ident}}&
337get${{dm.ident}}()
338{
339    return m_${{dm.ident}};
340}
341''')
342
343            #Set methods for each field
344            code('// Mutator methods for each field')
345            for dm in self.data_members.values():
346                code('''
347/** \\brief Mutator method for ${{dm.ident}} field */
348void
349set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}})
350{
351    m_${{dm.ident}} = local_${{dm.ident}};
352}
353''')
354
355        code('void print(std::ostream& out) const;')
356        code.dedent()
357        code('  //private:')
358        code.indent()
359
360        # Data members for each field
361        for dm in self.data_members.values():
362            if "abstract" not in dm:
363                const = ""
364                init = ""
365
366                # global structure
367                if self.isGlobal:
368                    const = "static const "
369
370                # init value
371                if dm.init_code:
372                    # only global structure can have init value here
373                    assert self.isGlobal
374                    init = " = %s" % (dm.init_code)
375
376                if "desc" in dm:
377                    code('/** ${{dm["desc"]}} */')
378
379                code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init;')
380
381        if self.isMessage:
382            code('unsigned proc_id;')
383
384        code.dedent()
385        code('};')
386
387        code('''
388// Output operator declaration
389std::ostream&
390operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
391
392// Output operator definition
393extern inline 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        if self.isMessage:
423            code('Allocator<${{self.c_ident}}>* ${{self.c_ident}}::s_allocator_ptr = NULL;')
424        code('''
425/** \\brief Print the state of this object */
426void
427${{self.c_ident}}::print(ostream& out) const
428{
429    out << "[${{self.c_ident}}: ";
430''')
431
432        # For each field
433        code.indent()
434        for dm in self.data_members.values():
435            code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
436
437        if self.isMessage:
438            code('out << "Time = " << getTime() << " ";')
439        code.dedent()
440
441        # Trailer
442        code('''
443    out << "]";
444}''')
445
446        code.write(path, "%s.cc" % self.c_ident)
447
448    def printEnumHH(self, path):
449        code = self.symtab.codeFormatter()
450        code('''
451/** \\file ${{self.c_ident}}.hh
452 *
453 * Auto generated C++ code started by $__file__:$__line__
454 */
455
456#ifndef __${{self.c_ident}}_HH__
457#define __${{self.c_ident}}_HH__
458
459#include <iostream>
460#include <string>
461
462#include "mem/ruby/common/Global.hh"
463
464// Class definition
465/** \\enum ${{self.c_ident}}
466 *  \\brief ${{self.desc}}
467 */
468enum ${{self.c_ident}} {
469    ${{self.c_ident}}_FIRST,
470''')
471
472        code.indent()
473        # For each field
474        for i,(ident,enum) in enumerate(self.enums.iteritems()):
475            desc = enum.get("desc", "No description avaliable")
476            if i == 0:
477                init = ' = %s_FIRST' % self.c_ident
478            else:
479                init = ''
480            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
481        code.dedent()
482        code('''
483    ${{self.c_ident}}_NUM
484};
485
486// Code to convert from a string to the enumeration
487${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str);
488
489// Code to convert state to a string
490std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
491
492// Code to increment an enumeration type
493${{self.c_ident}} &operator++(${{self.c_ident}} &e);
494''')
495
496        # MachineType hack used to set the base component id for each Machine
497        if self.isMachineType:
498            code('''
499int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
500MachineType ${{self.c_ident}}_from_base_level(int);
501int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
502int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
503''')
504
505            for enum in self.enums.itervalues():
506                code('#define MACHINETYPE_${{enum.ident}} 1')
507
508        # Trailer
509        code('''
510std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
511
512#endif // __${{self.c_ident}}_HH__
513''')
514
515        code.write(path, "%s.hh" % self.c_ident)
516
517    def printEnumCC(self, path):
518        code = self.symtab.codeFormatter()
519        code('''
520/** \\file ${{self.c_ident}}.hh
521 *
522 * Auto generated C++ code started by $__file__:$__line__
523 */
524
525#include <iostream>
526#include <string>
527
528#include "mem/protocol/${{self.c_ident}}.hh"
529
530using namespace std;
531
532''')
533
534        if self.isMachineType:
535            for enum in self.enums.itervalues():
536                code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
537
538        code('''
539// Code for output operator
540ostream&
541operator<<(ostream& out, const ${{self.c_ident}}& obj)
542{
543    out << ${{self.c_ident}}_to_string(obj);
544    out << flush;
545    return out;
546}
547
548// Code to convert state to a string
549string
550${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
551{
552    switch(obj) {
553''')
554
555        # For each field
556        code.indent()
557        for enum in self.enums.itervalues():
558            code('  case ${{self.c_ident}}_${{enum.ident}}:')
559            code('    return "${{enum.ident}}";')
560        code.dedent()
561
562        # Trailer
563        code('''
564      default:
565        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
566        return "";
567    }
568}
569
570// Code to convert from a string to the enumeration
571${{self.c_ident}}
572string_to_${{self.c_ident}}(const string& str)
573{
574''')
575
576        # For each field
577        start = ""
578        code.indent()
579        for enum in self.enums.itervalues():
580            code('${start}if (str == "${{enum.ident}}") {')
581            code('    return ${{self.c_ident}}_${{enum.ident}};')
582            start = "} else "
583        code.dedent()
584
585        code('''
586    } else {
587        WARN_EXPR(str);
588        ERROR_MSG("Invalid string conversion for type ${{self.c_ident}}");
589    }
590}
591
592// Code to increment an enumeration type
593${{self.c_ident}}&
594operator++(${{self.c_ident}}& e)
595{
596    assert(e < ${{self.c_ident}}_NUM);
597    return e = ${{self.c_ident}}(e+1);
598}
599''')
600
601        # MachineType hack used to set the base level and number of
602        # components for each Machine
603        if self.isMachineType:
604            code('''
605/** \\brief returns the base vector index for each machine type to be
606  * used by NetDest
607  *
608  * \\return the base vector index for each machine type to be used by NetDest
609  * \\see NetDest.hh
610  */
611int
612${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
613{
614    switch(obj) {
615''')
616
617            # For each field
618            code.indent()
619            for i,enum in enumerate(self.enums.itervalues()):
620                code('  case ${{self.c_ident}}_${{enum.ident}}:')
621                code('    return $i;')
622            code.dedent()
623
624            # total num
625            code('''
626      case ${{self.c_ident}}_NUM:
627        return ${{len(self.enums)}};
628
629      default:
630        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
631        return -1;
632    }
633}
634
635/** \\brief returns the machine type for each base vector index used by NetDest
636 *
637 * \\return the MachineType
638 */
639MachineType
640${{self.c_ident}}_from_base_level(int type)
641{
642    switch(type) {
643''')
644
645            # For each field
646            code.indent()
647            for i,enum in enumerate(self.enums.itervalues()):
648                code('  case $i:')
649                code('    return ${{self.c_ident}}_${{enum.ident}};')
650            code.dedent()
651
652            # Trailer
653            code('''
654      default:
655        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
656        return MachineType_NUM;
657    }
658}
659
660/** \\brief The return value indicates the number of components created
661 * before a particular machine\'s components
662 *
663 * \\return the base number of components for each machine
664 */
665int
666${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
667{
668    int base = 0;
669    switch(obj) {
670''')
671
672            # For each field
673            code.indent()
674            code('  case ${{self.c_ident}}_NUM:')
675            for enum in reversed(self.enums.values()):
676                code('    base += ${{enum.ident}}_Controller::getNumControllers();')
677                code('  case ${{self.c_ident}}_${{enum.ident}}:')
678            code('    break;')
679            code.dedent()
680
681            code('''
682      default:
683        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
684        return -1;
685    }
686
687    return base;
688}
689
690/** \\brief returns the total number of components for each machine
691 * \\return the total number of components for each machine
692 */
693int
694${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
695{
696    switch(obj) {
697''')
698
699            # For each field
700            for enum in self.enums.itervalues():
701                code('''
702      case ${{self.c_ident}}_${{enum.ident}}:
703        return ${{enum.ident}}_Controller::getNumControllers();
704''')
705
706            # total num
707            code('''
708      case ${{self.c_ident}}_NUM:
709      default:
710        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
711        return -1;
712    }
713}
714''')
715
716        # Write the file
717        code.write(path, "%s.cc" % self.c_ident)
718
719__all__ = [ "Type" ]
720