Type.py revision 6877:2a1a3d916ca8
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 code_formatter, 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        if machine:
55            if self.isExternal or self.isPrimitive:
56                if "external_name" in self:
57                    self.c_ident = self["external_name"]
58            else:
59                # Append with machine name
60                self.c_ident = "%s_%s" % (machine, ident)
61
62        self.pairs.setdefault("desc", "No description avaliable")
63
64        # check for interface that this Type implements
65        if "interface" in self:
66            interface = self["interface"]
67            if interface in ("Message", "NetworkMessage"):
68                self["message"] = "yes"
69            if interface == "NetworkMessage":
70                self["networkmessage"] = "yes"
71
72        # FIXME - all of the following id comparisons are fragile hacks
73        if self.ident in ("CacheMemory", "NewCacheMemory",
74                          "TLCCacheMemory", "DNUCACacheMemory",
75                          "DNUCABankCacheMemory", "L2BankCacheMemory",
76                          "CompressedCacheMemory", "PrefetchCacheMemory"):
77            self["cache"] = "yes"
78
79        if self.ident in ("TBETable", "DNUCATBETable", "DNUCAStopTable"):
80            self["tbe"] = "yes"
81
82        if self.ident == "NewTBETable":
83            self["newtbe"] = "yes"
84
85        if self.ident == "TimerTable":
86            self["timer"] = "yes"
87
88        if self.ident == "DirectoryMemory":
89            self["dir"] = "yes"
90
91        if self.ident == "PersistentTable":
92            self["persistent"] = "yes"
93
94        if self.ident == "Prefetcher":
95            self["prefetcher"] = "yes"
96
97        if self.ident == "DNUCA_Movement":
98            self["mover"] = "yes"
99
100        self.isMachineType = (ident == "MachineType")
101
102        self.data_members = orderdict()
103
104        # Methods
105        self.methods = {}
106
107        # Enums
108        self.enums = orderdict()
109
110    @property
111    def isPrimitive(self):
112        return "primitive" in self
113    @property
114    def isNetworkMessage(self):
115        return "networkmessage" in self
116    @property
117    def isMessage(self):
118        return "message" in self
119    @property
120    def isBuffer(self):
121        return "buffer" in self
122    @property
123    def isInPort(self):
124        return "inport" in self
125    @property
126    def isOutPort(self):
127        return "outport" in self
128    @property
129    def isEnumeration(self):
130        return "enumeration" in self
131    @property
132    def isExternal(self):
133        return "external" in self
134    @property
135    def isGlobal(self):
136        return "global" in self
137    @property
138    def isInterface(self):
139        return "interface" in self
140
141    # Return false on error
142    def dataMemberAdd(self, ident, type, pairs, init_code):
143        if ident in self.data_members:
144            return False
145
146        member = DataMember(ident, type, pairs, init_code)
147        self.data_members[ident] = member
148
149        return True
150
151    def dataMemberType(self, ident):
152        return self.data_members[ident].type
153
154    def methodId(self, name, param_type_vec):
155        return '_'.join([name] + [ pt.c_ident for pt in param_type_vec ])
156
157    def methodAdd(self, name, return_type, param_type_vec):
158        ident = self.methodId(name, param_type_vec)
159        if ident in self.methods:
160            return False
161
162        self.methods[ident] = Method(return_type, param_type_vec)
163        return True
164
165    def enumAdd(self, ident, pairs):
166        if ident in self.enums:
167            return False
168
169        self.enums[ident] = Enumeration(ident, pairs)
170
171        # Add default
172        if "default" not in self:
173            self["default"] = "%s_NUM" % self.c_ident
174
175        return True
176
177    def writeCodeFiles(self, path):
178        if self.isExternal:
179            # Do nothing
180            pass
181        elif self.isEnumeration:
182            self.printEnumHH(path)
183            self.printEnumCC(path)
184        else:
185            # User defined structs and messages
186            self.printTypeHH(path)
187            self.printTypeCC(path)
188
189    def printTypeHH(self, path):
190        code = code_formatter()
191        code('''
192/** \\file ${{self.c_ident}}.hh
193 *
194 *
195 * Auto generated C++ code started by $__file__:$__line__
196 */
197
198#ifndef ${{self.c_ident}}_H
199#define ${{self.c_ident}}_H
200
201#include "mem/ruby/common/Global.hh"
202#include "mem/gems_common/Allocator.hh"
203''')
204
205        for dm in self.data_members.values():
206            if not dm.type.isPrimitive:
207                code('#include "mem/protocol/$0.hh"', dm.type.c_ident)
208
209        parent = ""
210        if "interface" in self:
211            code('#include "mem/protocol/$0.hh"', self["interface"])
212            parent = " :  public %s" % self["interface"]
213
214        code('''
215$klass ${{self.c_ident}}$parent {
216  public:
217    ${{self.c_ident}}()
218''', klass="class")
219
220        # Call superclass constructor
221        if "interface" in self:
222            code('        : ${{self["interface"]}}()')
223
224        code.indent()
225        code("{")
226        if not self.isGlobal:
227            code.indent()
228            for dm in self.data_members.values():
229                ident = dm.ident
230                if "default" in dm:
231                    # look for default value
232                    code('m_$ident = ${{dm["default"]}}; // default for this field')
233                elif "default" in dm.type:
234                    # Look for the type default
235                    tid = dm.type.c_ident
236                    code('m_$ident = ${{dm.type["default"]}}; // default value of $tid')
237                else:
238                    code('// m_$ident has no default')
239            code.dedent()
240        code('}')
241
242        # ******** Default destructor ********
243        code('~${{self.c_ident}}() { };')
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"]}}* create() {
277    return new ${{self.c_ident}}();
278}
279''')
280
281        # ******** Message member functions ********
282        # FIXME: those should be moved into slicc file, slicc should
283        # support more of the c++ class inheritance
284
285        if self.isMessage:
286            code('''
287Message* clone() const { checkAllocator(); return s_allocator_ptr->allocate(*this); }
288void destroy() { checkAllocator(); s_allocator_ptr->deallocate(this); }
289static Allocator<${{self.c_ident}}>* s_allocator_ptr;
290static void checkAllocator() { if (s_allocator_ptr == NULL) { s_allocator_ptr = new Allocator<${{self.c_ident}}>; }}
291''')
292
293        if not self.isGlobal:
294            # const Get methods for each field
295            code('// Const accessors methods for each field')
296            for dm in self.data_members.values():
297                code('''
298/** \\brief Const accessor method for ${{dm.ident}} field.
299 *  \\return ${{dm.ident}} field
300 */
301const ${{dm.type.c_ident}}& get${{dm.ident}}() const { return m_${{dm.ident}}; }
302''')
303
304            # Non-const Get methods for each field
305            code('// Non const Accessors methods for each field')
306            for dm in self.data_members.values():
307                code('''
308/** \\brief Non-const accessor method for ${{dm.ident}} field.
309 *  \\return ${{dm.ident}} field
310 */
311${{dm.type.c_ident}}& get${{dm.ident}}() { return m_${{dm.ident}}; }
312''')
313
314            #Set methods for each field
315            code('// Mutator methods for each field')
316            for dm in self.data_members.values():
317                code('''
318/** \\brief Mutator method for ${{dm.ident}} field */
319void set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}}) { m_${{dm.ident}} = local_${{dm.ident}}; }
320''')
321
322        code('void print(ostream& out) const;')
323        code.dedent()
324        code('  //private:')
325        code.indent()
326
327        # Data members for each field
328        for dm in self.data_members.values():
329            if "abstract" not in dm:
330                const = ""
331                init = ""
332
333                # global structure
334                if self.isGlobal:
335                    const = "static const "
336
337                # init value
338                if dm.init_code:
339                    # only global structure can have init value here
340                    assert self.isGlobal
341                    init = " = %s" % (dm.init_code)
342
343                desc = ""
344                if "desc" in dm:
345                    desc = '/**< %s */' % dm["desc"]
346
347                code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init; $desc')
348
349        if self.isMessage:
350            code('unsigned proc_id;')
351
352        code.dedent()
353        code('};')
354
355        code('''
356// Output operator declaration
357ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj);
358
359// Output operator definition
360extern inline
361ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj)
362{
363    obj.print(out);
364    out << flush;
365    return out;
366}
367
368#endif // ${{self.c_ident}}_H
369''')
370
371        code.write(path, "%s.hh" % self.c_ident)
372
373    def printTypeCC(self, path):
374        code = code_formatter()
375
376        code('''
377/** \\file ${{self.c_ident}}.cc
378 *
379 * Auto generated C++ code started by $__file__:$__line__
380 */
381
382#include "mem/protocol/${{self.c_ident}}.hh"
383''')
384
385        if self.isMessage:
386            code('Allocator<${{self.c_ident}}>* ${{self.c_ident}}::s_allocator_ptr = NULL;')
387        code('''
388/** \\brief Print the state of this object */
389void ${{self.c_ident}}::print(ostream& out) const
390{
391    out << "[${{self.c_ident}}: ";
392''')
393
394        # For each field
395        code.indent()
396        for dm in self.data_members.values():
397            code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
398
399        if self.isMessage:
400            code('out << "Time = " << getTime() << " ";')
401        code.dedent()
402
403        # Trailer
404        code('''
405    out << "]";
406}''')
407
408        code.write(path, "%s.cc" % self.c_ident)
409
410    def printEnumHH(self, path):
411        code = code_formatter()
412        code('''
413/** \\file ${{self.c_ident}}.hh
414 *
415 * Auto generated C++ code started by $__file__:$__line__
416 */
417#ifndef ${{self.c_ident}}_H
418#define ${{self.c_ident}}_H
419
420#include "mem/ruby/common/Global.hh"
421
422/** \\enum ${{self.c_ident}}
423 *  \\brief ${{self.desc}}
424 */
425enum ${{self.c_ident}} {
426    ${{self.c_ident}}_FIRST,
427''')
428
429        code.indent()
430        # For each field
431        for i,(ident,enum) in enumerate(self.enums.iteritems()):
432            desc = enum.get("desc", "No description avaliable")
433            if i == 0:
434                init = ' = %s_FIRST' % self.c_ident
435            else:
436                init = ''
437            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
438        code.dedent()
439        code('''
440    ${{self.c_ident}}_NUM
441};
442${{self.c_ident}} string_to_${{self.c_ident}}(const string& str);
443string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
444${{self.c_ident}} &operator++(${{self.c_ident}} &e);
445''')
446
447        # MachineType hack used to set the base component id for each Machine
448        if self.isMachineType:
449            code('''
450int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
451MachineType ${{self.c_ident}}_from_base_level(int);
452int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
453int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
454''')
455
456            for enum in self.enums.itervalues():
457                code('#define MACHINETYPE_${{enum.ident}} 1')
458
459        # Trailer
460        code('''
461ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj);
462
463#endif // ${{self.c_ident}}_H
464''')
465
466        code.write(path, "%s.hh" % self.c_ident)
467
468    def printEnumCC(self, path):
469        code = code_formatter()
470        code('''
471/** \\file ${{self.c_ident}}.hh
472 *
473 * Auto generated C++ code started by $__file__:$__line__
474 */
475
476#include "mem/protocol/${{self.c_ident}}.hh"
477
478''')
479
480        if self.isMachineType:
481            for enum in self.enums.itervalues():
482                code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
483
484        code('''
485ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj)
486{
487    out << ${{self.c_ident}}_to_string(obj);
488    out << flush;
489    return out;
490}
491
492string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
493{
494    switch(obj) {
495''')
496
497        # For each field
498        code.indent()
499        for enum in self.enums.itervalues():
500            code('  case ${{self.c_ident}}_${{enum.ident}}:')
501            code('    return "${{enum.ident}}";')
502        code.dedent()
503
504        # Trailer
505        code('''
506      default:
507        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
508        return "";
509    }
510}
511
512${{self.c_ident}} string_to_${{self.c_ident}}(const string& str)
513{
514''')
515
516        # For each field
517        code.indent()
518        code("if (false) {")
519        start = "} else "
520        for enum in self.enums.itervalues():
521            code('${start}if (str == "${{enum.ident}}") {')
522            code('    return ${{self.c_ident}}_${{enum.ident}};')
523        code.dedent()
524
525        code('''
526    } else {
527        WARN_EXPR(str);
528        ERROR_MSG("Invalid string conversion for type ${{self.c_ident}}");
529    }
530}
531
532${{self.c_ident}}& operator++(${{self.c_ident}}& e) {
533    assert(e < ${{self.c_ident}}_NUM);
534    return e = ${{self.c_ident}}(e+1);
535}
536''')
537
538        # MachineType hack used to set the base level and number of
539        # components for each Machine
540        if self.isMachineType:
541            code('''
542/** \\brief returns the base vector index for each machine type to be used by NetDest
543  *
544  * \\return the base vector index for each machine type to be used by NetDest
545  * \\see NetDest.hh
546  */
547int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
548{
549    switch(obj) {
550''')
551
552            # For each field
553            code.indent()
554            for i,enum in enumerate(self.enums.itervalues()):
555                code('  case ${{self.c_ident}}_${{enum.ident}}:')
556                code('    return $i;')
557            code.dedent()
558
559            # total num
560            code('''
561      case ${{self.c_ident}}_NUM:
562        return ${{len(self.enums)}};
563
564      default:
565        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
566        return -1;
567    }
568}
569
570/** \\brief returns the machine type for each base vector index used by NetDest
571 *
572 * \\return the MachineTYpe
573 */
574MachineType ${{self.c_ident}}_from_base_level(int type)
575{
576    switch(type) {
577''')
578
579            # For each field
580            code.indent()
581            for i,enum in enumerate(self.enums.itervalues()):
582                code('  case $i:')
583                code('    return ${{self.c_ident}}_${{enum.ident}};')
584            code.dedent()
585
586            # Trailer
587            code('''
588      default:
589        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
590        return MachineType_NUM;
591    }
592}
593
594/** \\brief The return value indicates the number of components created
595 * before a particular machine\'s components
596 *
597 * \\return the base number of components for each machine
598 */
599int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
600{
601    int base = 0;
602    switch(obj) {
603''')
604
605            # For each field
606            code.indent()
607            code('  case ${{self.c_ident}}_NUM:')
608            for enum in reversed(self.enums.values()):
609                code('    base += ${{enum.ident}}_Controller::getNumControllers();')
610                code('  case ${{self.c_ident}}_${{enum.ident}}:')
611            code('    break;')
612            code.dedent()
613
614            code('''
615      default:
616        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
617        return -1;
618    }
619
620    return base;
621}
622
623/** \\brief returns the total number of components for each machine
624 * \\return the total number of components for each machine
625 */
626int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
627{
628    switch(obj) {
629''')
630
631            # For each field
632            for enum in self.enums.itervalues():
633                code('''
634      case ${{self.c_ident}}_${{enum.ident}}:
635        return ${{enum.ident}}_Controller::getNumControllers();
636''')
637
638            # total num
639            code('''
640      case ${{self.c_ident}}_NUM:
641      default:
642        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
643        return -1;
644    }
645}
646''')
647
648        # Write the file
649        code.write(path, "%s.cc" % self.c_ident)
650
651__all__ = [ "Type" ]
652