Type.py revision 6657:ef5fae93a3b2
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            init = ' = %s_FIRST' % self.c_ident if i == 0 else ''
434
435            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
436        code.dedent()
437        code('''
438    ${{self.c_ident}}_NUM
439};
440${{self.c_ident}} string_to_${{self.c_ident}}(const string& str);
441string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
442${{self.c_ident}} &operator++(${{self.c_ident}} &e);
443''')
444
445        # MachineType hack used to set the base component id for each Machine
446        if self.isMachineType:
447            code('''
448int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
449MachineType ${{self.c_ident}}_from_base_level(int);
450int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
451int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
452''')
453
454            for enum in self.enums.itervalues():
455                code('#define MACHINETYPE_${{enum.ident}} 1')
456
457        # Trailer
458        code('''
459ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj);
460
461#endif // ${{self.c_ident}}_H
462''')
463
464        code.write(path, "%s.hh" % self.c_ident)
465
466    def printEnumCC(self, path):
467        code = code_formatter()
468        code('''
469/** \\file ${{self.c_ident}}.hh
470 *
471 * Auto generated C++ code started by $__file__:$__line__
472 */
473
474#include "mem/protocol/${{self.c_ident}}.hh"
475
476''')
477
478        if self.isMachineType:
479            code('#include "mem/protocol/ControllerFactory.hh"')
480            for enum in self.enums.itervalues():
481                code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
482
483        code('''
484ostream& operator<<(ostream& out, const ${{self.c_ident}}& obj)
485{
486    out << ${{self.c_ident}}_to_string(obj);
487    out << flush;
488    return out;
489}
490
491string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
492{
493    switch(obj) {
494''')
495
496        # For each field
497        code.indent()
498        for enum in self.enums.itervalues():
499            code('  case ${{self.c_ident}}_${{enum.ident}}:')
500            code('    return "${{enum.ident}}";')
501        code.dedent()
502
503        # Trailer
504        code('''
505      default:
506        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
507        return "";
508    }
509}
510
511${{self.c_ident}} string_to_${{self.c_ident}}(const string& str)
512{
513''')
514
515        # For each field
516        code.indent()
517        code("if (false) {")
518        start = "} else "
519        for enum in self.enums.itervalues():
520            code('${start}if (str == "${{enum.ident}}") {')
521            code('    return ${{self.c_ident}}_${{enum.ident}};')
522        code.dedent()
523
524        code('''
525    } else {
526        WARN_EXPR(str);
527        ERROR_MSG("Invalid string conversion for type ${{self.c_ident}}");
528    }
529}
530
531${{self.c_ident}}& operator++(${{self.c_ident}}& e) {
532    assert(e < ${{self.c_ident}}_NUM);
533    return e = ${{self.c_ident}}(e+1);
534}
535''')
536
537        # MachineType hack used to set the base level and number of
538        # components for each Machine
539        if self.isMachineType:
540            code('''
541/** \\brief returns the base vector index for each machine type to be used by NetDest
542  *
543  * \\return the base vector index for each machine type to be used by NetDest
544  * \\see NetDest.hh
545  */
546int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
547{
548    switch(obj) {
549''')
550
551            # For each field
552            code.indent()
553            for i,enum in enumerate(self.enums.itervalues()):
554                code('  case ${{self.c_ident}}_${{enum.ident}}:')
555                code('    return $i;')
556            code.dedent()
557
558            # total num
559            code('''
560      case ${{self.c_ident}}_NUM:
561        return ${{len(self.enums)}};
562
563      default:
564        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
565        return -1;
566    }
567}
568
569/** \\brief returns the machine type for each base vector index used by NetDest
570 *
571 * \\return the MachineTYpe
572 */
573MachineType ${{self.c_ident}}_from_base_level(int type)
574{
575    switch(type) {
576''')
577
578            # For each field
579            code.indent()
580            for i,enum in enumerate(self.enums.itervalues()):
581                code('  case $i:')
582                code('    return ${{self.c_ident}}_${{enum.ident}};')
583            code.dedent()
584
585            # Trailer
586            code('''
587      default:
588        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
589        return MachineType_NUM;
590    }
591}
592
593/** \\brief The return value indicates the number of components created
594 * before a particular machine\'s components
595 *
596 * \\return the base number of components for each machine
597 */
598int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
599{
600    int base = 0;
601    switch(obj) {
602''')
603
604            # For each field
605            code.indent()
606            code('  case ${{self.c_ident}}_NUM:')
607            for enum in reversed(self.enums.values()):
608                code('    base += ${{enum.ident}}_Controller::getNumControllers();')
609                code('  case ${{self.c_ident}}_${{enum.ident}}:')
610            code('    break;')
611            code.dedent()
612
613            code('''
614      default:
615        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
616        return -1;
617    }
618
619    return base;
620}
621
622/** \\brief returns the total number of components for each machine
623 * \\return the total number of components for each machine
624 */
625int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
626{
627    switch(obj) {
628''')
629
630            # For each field
631            for enum in self.enums.itervalues():
632                code('''
633      case ${{self.c_ident}}_${{enum.ident}}:
634        return ${{enum.ident}}_Controller::getNumControllers();
635''')
636
637            # total num
638            code('''
639      case ${{self.c_ident}}_NUM:
640      default:
641        ERROR_MSG("Invalid range for type ${{self.c_ident}}");
642        return -1;
643    }
644}
645''')
646
647        # Write the file
648        code.write(path, "%s.cc" % self.c_ident)
649
650__all__ = [ "Type" ]
651