Type.py revision 7055
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('''
388inline std::ostream&
389operator<<(std::ostream& out, const ${{self.c_ident}}& obj)
390{
391    obj.print(out);
392    out << std::flush;
393    return out;
394}
395
396#endif // __${{self.c_ident}}_HH__
397''')
398
399        code.write(path, "%s.hh" % self.c_ident)
400
401    def printTypeCC(self, path):
402        code = self.symtab.codeFormatter()
403
404        code('''
405/** \\file ${{self.c_ident}}.cc
406 *
407 * Auto generated C++ code started by $__file__:$__line__
408 */
409
410#include <iostream>
411
412#include "mem/protocol/${{self.c_ident}}.hh"
413
414using namespace std;
415''')
416
417        if self.isMessage:
418            code('Allocator<${{self.c_ident}}>* ${{self.c_ident}}::s_allocator_ptr = NULL;')
419        code('''
420/** \\brief Print the state of this object */
421void
422${{self.c_ident}}::print(ostream& out) const
423{
424    out << "[${{self.c_ident}}: ";
425''')
426
427        # For each field
428        code.indent()
429        for dm in self.data_members.values():
430            code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
431
432        if self.isMessage:
433            code('out << "Time = " << getTime() << " ";')
434        code.dedent()
435
436        # Trailer
437        code('''
438    out << "]";
439}''')
440
441        code.write(path, "%s.cc" % self.c_ident)
442
443    def printEnumHH(self, path):
444        code = self.symtab.codeFormatter()
445        code('''
446/** \\file ${{self.c_ident}}.hh
447 *
448 * Auto generated C++ code started by $__file__:$__line__
449 */
450
451#ifndef __${{self.c_ident}}_HH__
452#define __${{self.c_ident}}_HH__
453
454#include <iostream>
455#include <string>
456
457#include "mem/ruby/common/Global.hh"
458
459// Class definition
460/** \\enum ${{self.c_ident}}
461 *  \\brief ${{self.desc}}
462 */
463enum ${{self.c_ident}} {
464    ${{self.c_ident}}_FIRST,
465''')
466
467        code.indent()
468        # For each field
469        for i,(ident,enum) in enumerate(self.enums.iteritems()):
470            desc = enum.get("desc", "No description avaliable")
471            if i == 0:
472                init = ' = %s_FIRST' % self.c_ident
473            else:
474                init = ''
475            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
476        code.dedent()
477        code('''
478    ${{self.c_ident}}_NUM
479};
480
481// Code to convert from a string to the enumeration
482${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str);
483
484// Code to convert state to a string
485std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
486
487// Code to increment an enumeration type
488${{self.c_ident}} &operator++(${{self.c_ident}} &e);
489''')
490
491        # MachineType hack used to set the base component id for each Machine
492        if self.isMachineType:
493            code('''
494int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
495MachineType ${{self.c_ident}}_from_base_level(int);
496int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
497int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
498''')
499
500            for enum in self.enums.itervalues():
501                code('#define MACHINETYPE_${{enum.ident}} 1')
502
503        # Trailer
504        code('''
505std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
506
507#endif // __${{self.c_ident}}_HH__
508''')
509
510        code.write(path, "%s.hh" % self.c_ident)
511
512    def printEnumCC(self, path):
513        code = self.symtab.codeFormatter()
514        code('''
515/** \\file ${{self.c_ident}}.hh
516 *
517 * Auto generated C++ code started by $__file__:$__line__
518 */
519
520#include <iostream>
521#include <string>
522
523#include "mem/protocol/${{self.c_ident}}.hh"
524
525using namespace std;
526
527''')
528
529        if self.isMachineType:
530            for enum in self.enums.itervalues():
531                code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
532
533        code('''
534// Code for output operator
535ostream&
536operator<<(ostream& out, const ${{self.c_ident}}& obj)
537{
538    out << ${{self.c_ident}}_to_string(obj);
539    out << flush;
540    return out;
541}
542
543// Code to convert state to a string
544string
545${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
546{
547    switch(obj) {
548''')
549
550        # For each field
551        code.indent()
552        for enum in self.enums.itervalues():
553            code('  case ${{self.c_ident}}_${{enum.ident}}:')
554            code('    return "${{enum.ident}}";')
555        code.dedent()
556
557        # Trailer
558        code('''
559      default:
560        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
561        return "";
562    }
563}
564
565// Code to convert from a string to the enumeration
566${{self.c_ident}}
567string_to_${{self.c_ident}}(const string& str)
568{
569''')
570
571        # For each field
572        start = ""
573        code.indent()
574        for enum in self.enums.itervalues():
575            code('${start}if (str == "${{enum.ident}}") {')
576            code('    return ${{self.c_ident}}_${{enum.ident}};')
577            start = "} else "
578        code.dedent()
579
580        code('''
581    } else {
582        WARN_EXPR(str);
583        ERROR_MSG("Invalid string conversion for type ${{self.c_ident}}");
584    }
585}
586
587// Code to increment an enumeration type
588${{self.c_ident}}&
589operator++(${{self.c_ident}}& e)
590{
591    assert(e < ${{self.c_ident}}_NUM);
592    return e = ${{self.c_ident}}(e+1);
593}
594''')
595
596        # MachineType hack used to set the base level and number of
597        # components for each Machine
598        if self.isMachineType:
599            code('''
600/** \\brief returns the base vector index for each machine type to be
601  * used by NetDest
602  *
603  * \\return the base vector index for each machine type to be used by NetDest
604  * \\see NetDest.hh
605  */
606int
607${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
608{
609    switch(obj) {
610''')
611
612            # For each field
613            code.indent()
614            for i,enum in enumerate(self.enums.itervalues()):
615                code('  case ${{self.c_ident}}_${{enum.ident}}:')
616                code('    return $i;')
617            code.dedent()
618
619            # total num
620            code('''
621      case ${{self.c_ident}}_NUM:
622        return ${{len(self.enums)}};
623
624      default:
625        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
626        return -1;
627    }
628}
629
630/** \\brief returns the machine type for each base vector index used by NetDest
631 *
632 * \\return the MachineType
633 */
634MachineType
635${{self.c_ident}}_from_base_level(int type)
636{
637    switch(type) {
638''')
639
640            # For each field
641            code.indent()
642            for i,enum in enumerate(self.enums.itervalues()):
643                code('  case $i:')
644                code('    return ${{self.c_ident}}_${{enum.ident}};')
645            code.dedent()
646
647            # Trailer
648            code('''
649      default:
650        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
651        return MachineType_NUM;
652    }
653}
654
655/** \\brief The return value indicates the number of components created
656 * before a particular machine\'s components
657 *
658 * \\return the base number of components for each machine
659 */
660int
661${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
662{
663    int base = 0;
664    switch(obj) {
665''')
666
667            # For each field
668            code.indent()
669            code('  case ${{self.c_ident}}_NUM:')
670            for enum in reversed(self.enums.values()):
671                code('    base += ${{enum.ident}}_Controller::getNumControllers();')
672                code('  case ${{self.c_ident}}_${{enum.ident}}:')
673            code('    break;')
674            code.dedent()
675
676            code('''
677      default:
678        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
679        return -1;
680    }
681
682    return base;
683}
684
685/** \\brief returns the total number of components for each machine
686 * \\return the total number of components for each machine
687 */
688int
689${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
690{
691    switch(obj) {
692''')
693
694            # For each field
695            for enum in self.enums.itervalues():
696                code('''
697      case ${{self.c_ident}}_${{enum.ident}}:
698        return ${{enum.ident}}_Controller::getNumControllers();
699''')
700
701            # total num
702            code('''
703      case ${{self.c_ident}}_NUM:
704      default:
705        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
706        return -1;
707    }
708}
709''')
710
711        # Write the file
712        code.write(path, "%s.cc" % self.c_ident)
713
714__all__ = [ "Type" ]
715