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