Type.py revision 6999:f226c098c393
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}}_H
203#define ${{self.c_ident}}_H
204
205#include "mem/ruby/common/Global.hh"
206#include "mem/gems_common/Allocator.hh"
207''')
208
209        for dm in self.data_members.values():
210            if not dm.type.isPrimitive:
211                code('#include "mem/protocol/$0.hh"', dm.type.c_ident)
212
213        parent = ""
214        if "interface" in self:
215            code('#include "mem/protocol/$0.hh"', self["interface"])
216            parent = " :  public %s" % self["interface"]
217
218        code('''
219$klass ${{self.c_ident}}$parent {
220  public:
221    ${{self.c_ident}}()
222''', klass="class")
223
224        # Call superclass constructor
225        if "interface" in self:
226            code('        : ${{self["interface"]}}()')
227
228        code.indent()
229        code("{")
230        if not self.isGlobal:
231            code.indent()
232            for dm in self.data_members.values():
233                ident = dm.ident
234                if "default" in dm:
235                    # look for default value
236                    code('m_$ident = ${{dm["default"]}}; // default for this field')
237                elif "default" in dm.type:
238                    # Look for the type default
239                    tid = dm.type.c_ident
240                    code('m_$ident = ${{dm.type["default"]}}; // default value of $tid')
241                else:
242                    code('// m_$ident has no default')
243            code.dedent()
244        code('}')
245
246        # ******** Default destructor ********
247        code('~${{self.c_ident}}() { };')
248
249        # ******** Full init constructor ********
250        if not self.isGlobal:
251            params = [ 'const %s& local_%s' % (dm.type.c_ident, dm.ident) \
252                       for dm in self.data_members.itervalues() ]
253
254            if self.isMessage:
255                params.append('const unsigned local_proc_id')
256
257            params = ', '.join(params)
258            code('${{self.c_ident}}($params)')
259
260            # Call superclass constructor
261            if "interface" in self:
262                code('    : ${{self["interface"]}}()')
263
264            code('{')
265            code.indent()
266            for dm in self.data_members.values():
267                code('m_${{dm.ident}} = local_${{dm.ident}};')
268                if "nextLineCallHack" in dm:
269                    code('m_${{dm.ident}}${{dm["nextLineCallHack"]}};')
270
271            if self.isMessage:
272                code('proc_id = local_proc_id;')
273
274            code.dedent()
275            code('}')
276
277        # create a static factory method
278        if "interface" in self:
279            code('''
280static ${{self["interface"]}}* create() {
281    return new ${{self.c_ident}}();
282}
283''')
284
285        # ******** Message member functions ********
286        # FIXME: those should be moved into slicc file, slicc should
287        # support more of the c++ class inheritance
288
289        if self.isMessage:
290            code('''
291Message* clone() const { checkAllocator(); return s_allocator_ptr->allocate(*this); }
292void destroy() { checkAllocator(); s_allocator_ptr->deallocate(this); }
293static Allocator<${{self.c_ident}}>* s_allocator_ptr;
294static void checkAllocator() { if (s_allocator_ptr == NULL) { s_allocator_ptr = new Allocator<${{self.c_ident}}>; }}
295''')
296
297        if not self.isGlobal:
298            # const Get methods for each field
299            code('// Const accessors methods for each field')
300            for dm in self.data_members.values():
301                code('''
302/** \\brief Const accessor method for ${{dm.ident}} field.
303 *  \\return ${{dm.ident}} field
304 */
305const ${{dm.type.c_ident}}& get${{dm.ident}}() const { return m_${{dm.ident}}; }
306''')
307
308            # Non-const Get methods for each field
309            code('// Non const Accessors methods for each field')
310            for dm in self.data_members.values():
311                code('''
312/** \\brief Non-const accessor method for ${{dm.ident}} field.
313 *  \\return ${{dm.ident}} field
314 */
315${{dm.type.c_ident}}& get${{dm.ident}}() { return m_${{dm.ident}}; }
316''')
317
318            #Set methods for each field
319            code('// Mutator methods for each field')
320            for dm in self.data_members.values():
321                code('''
322/** \\brief Mutator method for ${{dm.ident}} field */
323void set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}}) { m_${{dm.ident}} = local_${{dm.ident}}; }
324''')
325
326        code('void print(ostream& out) const;')
327        code.dedent()
328        code('  //private:')
329        code.indent()
330
331        # Data members for each field
332        for dm in self.data_members.values():
333            if "abstract" not in dm:
334                const = ""
335                init = ""
336
337                # global structure
338                if self.isGlobal:
339                    const = "static const "
340
341                # init value
342                if dm.init_code:
343                    # only global structure can have init value here
344                    assert self.isGlobal
345                    init = " = %s" % (dm.init_code)
346
347                desc = ""
348                if "desc" in dm:
349                    desc = '/**< %s */' % dm["desc"]
350
351                code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init; $desc')
352
353        if self.isMessage:
354            code('unsigned proc_id;')
355
356        code.dedent()
357        code('};')
358
359        code('''
360// Output operator declaration
361ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj);
362
363// Output operator definition
364extern inline
365ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj)
366{
367    obj.print(out);
368    out << flush;
369    return out;
370}
371
372#endif // ${{self.c_ident}}_H
373''')
374
375        code.write(path, "%s.hh" % self.c_ident)
376
377    def printTypeCC(self, path):
378        code = self.symtab.codeFormatter()
379
380        code('''
381/** \\file ${{self.c_ident}}.cc
382 *
383 * Auto generated C++ code started by $__file__:$__line__
384 */
385
386#include "mem/protocol/${{self.c_ident}}.hh"
387''')
388
389        if self.isMessage:
390            code('Allocator<${{self.c_ident}}>* ${{self.c_ident}}::s_allocator_ptr = NULL;')
391        code('''
392/** \\brief Print the state of this object */
393void ${{self.c_ident}}::print(ostream& out) const
394{
395    out << "[${{self.c_ident}}: ";
396''')
397
398        # For each field
399        code.indent()
400        for dm in self.data_members.values():
401            code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
402
403        if self.isMessage:
404            code('out << "Time = " << getTime() << " ";')
405        code.dedent()
406
407        # Trailer
408        code('''
409    out << "]";
410}''')
411
412        code.write(path, "%s.cc" % self.c_ident)
413
414    def printEnumHH(self, path):
415        code = self.symtab.codeFormatter()
416        code('''
417/** \\file ${{self.c_ident}}.hh
418 *
419 * Auto generated C++ code started by $__file__:$__line__
420 */
421#ifndef ${{self.c_ident}}_H
422#define ${{self.c_ident}}_H
423
424#include "mem/ruby/common/Global.hh"
425
426/** \\enum ${{self.c_ident}}
427 *  \\brief ${{self.desc}}
428 */
429enum ${{self.c_ident}} {
430    ${{self.c_ident}}_FIRST,
431''')
432
433        code.indent()
434        # For each field
435        for i,(ident,enum) in enumerate(self.enums.iteritems()):
436            desc = enum.get("desc", "No description avaliable")
437            if i == 0:
438                init = ' = %s_FIRST' % self.c_ident
439            else:
440                init = ''
441            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
442        code.dedent()
443        code('''
444    ${{self.c_ident}}_NUM
445};
446${{self.c_ident}} string_to_${{self.c_ident}}(const string& str);
447string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
448${{self.c_ident}} &operator++(${{self.c_ident}} &e);
449''')
450
451        # MachineType hack used to set the base component id for each Machine
452        if self.isMachineType:
453            code('''
454int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
455MachineType ${{self.c_ident}}_from_base_level(int);
456int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
457int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
458''')
459
460            for enum in self.enums.itervalues():
461                code('#define MACHINETYPE_${{enum.ident}} 1')
462
463        # Trailer
464        code('''
465ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj);
466
467#endif // ${{self.c_ident}}_H
468''')
469
470        code.write(path, "%s.hh" % self.c_ident)
471
472    def printEnumCC(self, path):
473        code = self.symtab.codeFormatter()
474        code('''
475/** \\file ${{self.c_ident}}.hh
476 *
477 * Auto generated C++ code started by $__file__:$__line__
478 */
479
480#include "mem/protocol/${{self.c_ident}}.hh"
481
482''')
483
484        if self.isMachineType:
485            for enum in self.enums.itervalues():
486                code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
487
488        code('''
489ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj)
490{
491    out << ${{self.c_ident}}_to_string(obj);
492    out << flush;
493    return out;
494}
495
496string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
497{
498    switch(obj) {
499''')
500
501        # For each field
502        code.indent()
503        for enum in self.enums.itervalues():
504            code('  case ${{self.c_ident}}_${{enum.ident}}:')
505            code('    return "${{enum.ident}}";')
506        code.dedent()
507
508        # Trailer
509        code('''
510      default:
511        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
512        return "";
513    }
514}
515
516${{self.c_ident}} string_to_${{self.c_ident}}(const string& str)
517{
518''')
519
520        # For each field
521        code.indent()
522        code("if (false) {")
523        start = "} else "
524        for enum in self.enums.itervalues():
525            code('${start}if (str == "${{enum.ident}}") {')
526            code('    return ${{self.c_ident}}_${{enum.ident}};')
527        code.dedent()
528
529        code('''
530    } else {
531        WARN_EXPR(str);
532        ERROR_MSG("Invalid string conversion for type ${{self.c_ident}}");
533    }
534}
535
536${{self.c_ident}}& operator++(${{self.c_ident}}& e) {
537    assert(e < ${{self.c_ident}}_NUM);
538    return e = ${{self.c_ident}}(e+1);
539}
540''')
541
542        # MachineType hack used to set the base level and number of
543        # components for each Machine
544        if self.isMachineType:
545            code('''
546/** \\brief returns the base vector index for each machine type to be used by NetDest
547  *
548  * \\return the base vector index for each machine type to be used by NetDest
549  * \\see NetDest.hh
550  */
551int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
552{
553    switch(obj) {
554''')
555
556            # For each field
557            code.indent()
558            for i,enum in enumerate(self.enums.itervalues()):
559                code('  case ${{self.c_ident}}_${{enum.ident}}:')
560                code('    return $i;')
561            code.dedent()
562
563            # total num
564            code('''
565      case ${{self.c_ident}}_NUM:
566        return ${{len(self.enums)}};
567
568      default:
569        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
570        return -1;
571    }
572}
573
574/** \\brief returns the machine type for each base vector index used by NetDest
575 *
576 * \\return the MachineTYpe
577 */
578MachineType ${{self.c_ident}}_from_base_level(int type)
579{
580    switch(type) {
581''')
582
583            # For each field
584            code.indent()
585            for i,enum in enumerate(self.enums.itervalues()):
586                code('  case $i:')
587                code('    return ${{self.c_ident}}_${{enum.ident}};')
588            code.dedent()
589
590            # Trailer
591            code('''
592      default:
593        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
594        return MachineType_NUM;
595    }
596}
597
598/** \\brief The return value indicates the number of components created
599 * before a particular machine\'s components
600 *
601 * \\return the base number of components for each machine
602 */
603int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
604{
605    int base = 0;
606    switch(obj) {
607''')
608
609            # For each field
610            code.indent()
611            code('  case ${{self.c_ident}}_NUM:')
612            for enum in reversed(self.enums.values()):
613                code('    base += ${{enum.ident}}_Controller::getNumControllers();')
614                code('  case ${{self.c_ident}}_${{enum.ident}}:')
615            code('    break;')
616            code.dedent()
617
618            code('''
619      default:
620        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
621        return -1;
622    }
623
624    return base;
625}
626
627/** \\brief returns the total number of components for each machine
628 * \\return the total number of components for each machine
629 */
630int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
631{
632    switch(obj) {
633''')
634
635            # For each field
636            for enum in self.enums.itervalues():
637                code('''
638      case ${{self.c_ident}}_${{enum.ident}}:
639        return ${{enum.ident}}_Controller::getNumControllers();
640''')
641
642            # total num
643            code('''
644      case ${{self.c_ident}}_NUM:
645      default:
646        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
647        return -1;
648    }
649}
650''')
651
652        # Write the file
653        code.write(path, "%s.cc" % self.c_ident)
654
655__all__ = [ "Type" ]
656