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 collections import OrderedDict
29
30from slicc.util import PairContainer
31from slicc.symbols.Symbol import Symbol
32from slicc.symbols.Var import Var
33
34class DataMember(Var):
35    def __init__(self, symtab, ident, location, type, code, pairs,
36                 machine, init_code):
37        super(DataMember, self).__init__(symtab, ident, location, type,
38                                         code, pairs, machine)
39        self.init_code = init_code
40
41class Enumeration(PairContainer):
42    def __init__(self, ident, pairs):
43        super(Enumeration, self).__init__(pairs)
44        self.ident = ident
45        self.primary = False
46
47class Type(Symbol):
48    def __init__(self, table, ident, location, pairs, machine=None):
49        super(Type, self).__init__(table, ident, location, pairs)
50        self.c_ident = ident
51        self.abstract_ident = ""
52        if machine:
53            if self.isExternal or self.isPrimitive:
54                if "external_name" in self:
55                    self.c_ident = self["external_name"]
56            else:
57                # Append with machine name
58                self.c_ident = "%s_%s" % (machine, ident)
59
60        self.pairs.setdefault("desc", "No description avaliable")
61
62        # check for interface that this Type implements
63        if "interface" in self:
64            interface = self["interface"]
65            if interface in ("Message"):
66                self["message"] = "yes"
67
68        # FIXME - all of the following id comparisons are fragile hacks
69        if self.ident in ("CacheMemory"):
70            self["cache"] = "yes"
71
72        if self.ident in ("TBETable"):
73            self["tbe"] = "yes"
74
75        if self.ident == "TimerTable":
76            self["timer"] = "yes"
77
78        if self.ident == "DirectoryMemory":
79            self["dir"] = "yes"
80
81        if self.ident == "PersistentTable":
82            self["persistent"] = "yes"
83
84        if self.ident == "Prefetcher":
85            self["prefetcher"] = "yes"
86
87        self.isMachineType = (ident == "MachineType")
88
89        self.isStateDecl = ("state_decl" in self)
90        self.statePermPairs = []
91
92        self.data_members = OrderedDict()
93        self.methods = {}
94        self.enums = OrderedDict()
95
96    @property
97    def isPrimitive(self):
98        return "primitive" in self
99
100    @property
101    def isMessage(self):
102        return "message" in self
103    @property
104    def isBuffer(self):
105        return "buffer" in self
106    @property
107    def isInPort(self):
108        return "inport" in self
109    @property
110    def isOutPort(self):
111        return "outport" in self
112    @property
113    def isEnumeration(self):
114        return "enumeration" in self
115    @property
116    def isExternal(self):
117        return "external" in self
118    @property
119    def isGlobal(self):
120        return "global" in self
121    @property
122    def isInterface(self):
123        return "interface" in self
124
125    # Return false on error
126    def addDataMember(self, ident, type, pairs, init_code):
127        if ident in self.data_members:
128            return False
129
130        member = DataMember(self.symtab, ident, self.location, type,
131                            "m_%s" % ident, pairs, None, init_code)
132
133        self.data_members[ident] = member
134        self.symtab.registerSym(ident, member)
135        return True
136
137    def dataMemberType(self, ident):
138        return self.data_members[ident].type
139
140    def methodId(self, name, param_type_vec):
141        return '_'.join([name] + [ pt.c_ident for pt in param_type_vec ])
142
143    def methodIdAbstract(self, name, param_type_vec):
144        return '_'.join([name] + [ pt.abstract_ident for pt in param_type_vec ])
145
146    def statePermPairAdd(self, state_name, perm_name):
147        self.statePermPairs.append([state_name, perm_name])
148
149    def addFunc(self, func):
150        ident = self.methodId(func.ident, func.param_types)
151        if ident in self.methods:
152            return False
153
154        self.methods[ident] = func
155        return True
156
157    def addEnum(self, ident, pairs):
158        if ident in self.enums:
159            return False
160
161        self.enums[ident] = Enumeration(ident, pairs)
162
163        # Add default
164        if "default" not in self:
165            self["default"] = "%s_NUM" % self.c_ident
166
167        return True
168
169    ## Used to check if an enum has been already used and therefore
170    ## should not be used again.
171    def checkEnum(self, ident):
172        if ident in self.enums and not self.enums[ident].primary:
173            self.enums[ident].primary = True
174            return True
175        return False
176
177    def writeCodeFiles(self, path, includes):
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 = self.symtab.codeFormatter()
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}}_HH__
199#define __${{self.c_ident}}_HH__
200
201#include <iostream>
202
203#include "mem/ruby/slicc_interface/RubySlicc_Util.hh"
204
205''')
206
207        for dm in self.data_members.values():
208            if not dm.type.isPrimitive:
209                code('#include "mem/ruby/protocol/$0.hh"', dm.type.c_ident)
210
211        parent = ""
212        if "interface" in self:
213            code('#include "mem/ruby/protocol/$0.hh"', self["interface"])
214            parent = " :  public %s" % self["interface"]
215
216        code('''
217$klass ${{self.c_ident}}$parent
218{
219  public:
220    ${{self.c_ident}}
221''', klass="class")
222
223        if self.isMessage:
224            code('(Tick curTime) : %s(curTime) {' % self["interface"])
225        else:
226            code('()\n\t\t{')
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        # ******** Copy constructor ********
246        if not self.isGlobal:
247            code('${{self.c_ident}}(const ${{self.c_ident}}&other)')
248
249            # Call superclass constructor
250            if "interface" in self:
251                code('    : ${{self["interface"]}}(other)')
252
253            code('{')
254            code.indent()
255
256            for dm in self.data_members.values():
257                code('m_${{dm.ident}} = other.m_${{dm.ident}};')
258
259            code.dedent()
260            code('}')
261
262        # ******** Full init constructor ********
263        if not self.isGlobal:
264            params = [ 'const %s& local_%s' % (dm.type.c_ident, dm.ident) \
265                       for dm in self.data_members.itervalues() ]
266            params = ', '.join(params)
267
268            if self.isMessage:
269                params = "const Tick curTime, " + params
270
271            code('${{self.c_ident}}($params)')
272
273            # Call superclass constructor
274            if "interface" in self:
275                if self.isMessage:
276                    code('    : ${{self["interface"]}}(curTime)')
277                else:
278                    code('    : ${{self["interface"]}}()')
279
280            code('{')
281            code.indent()
282            for dm in self.data_members.values():
283                code('m_${{dm.ident}} = local_${{dm.ident}};')
284
285            code.dedent()
286            code('}')
287
288        # create a clone member
289        if self.isMessage:
290            code('''
291MsgPtr
292clone() const
293{
294     return std::shared_ptr<Message>(new ${{self.c_ident}}(*this));
295}
296''')
297        else:
298            code('''
299${{self.c_ident}}*
300clone() const
301{
302     return new ${{self.c_ident}}(*this);
303}
304''')
305
306        if not self.isGlobal:
307            # const Get methods for each field
308            code('// Const accessors methods for each field')
309            for dm in self.data_members.values():
310                code('''
311/** \\brief Const accessor method for ${{dm.ident}} field.
312 *  \\return ${{dm.ident}} field
313 */
314const ${{dm.type.c_ident}}&
315get${{dm.ident}}() const
316{
317    return m_${{dm.ident}};
318}
319''')
320
321            # Non-const Get methods for each field
322            code('// Non const Accessors methods for each field')
323            for dm in self.data_members.values():
324                code('''
325/** \\brief Non-const accessor method for ${{dm.ident}} field.
326 *  \\return ${{dm.ident}} field
327 */
328${{dm.type.c_ident}}&
329get${{dm.ident}}()
330{
331    return m_${{dm.ident}};
332}
333''')
334
335            #Set methods for each field
336            code('// Mutator methods for each field')
337            for dm in self.data_members.values():
338                code('''
339/** \\brief Mutator method for ${{dm.ident}} field */
340void
341set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}})
342{
343    m_${{dm.ident}} = local_${{dm.ident}};
344}
345''')
346
347        code('void print(std::ostream& out) const;')
348        code.dedent()
349        code('  //private:')
350        code.indent()
351
352        # Data members for each field
353        for dm in self.data_members.values():
354            if "abstract" not in dm:
355                const = ""
356                init = ""
357
358                # global structure
359                if self.isGlobal:
360                    const = "static const "
361
362                # init value
363                if dm.init_code:
364                    # only global structure can have init value here
365                    assert self.isGlobal
366                    init = " = %s" % (dm.init_code)
367
368                if "desc" in dm:
369                    code('/** ${{dm["desc"]}} */')
370
371                code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init;')
372
373        # Prototypes for methods defined for the Type
374        for item in self.methods:
375            proto = self.methods[item].prototype
376            if proto:
377                code('$proto')
378
379        code.dedent()
380        code('};')
381
382        code('''
383inline std::ostream&
384operator<<(std::ostream& out, const ${{self.c_ident}}& obj)
385{
386    obj.print(out);
387    out << std::flush;
388    return out;
389}
390
391#endif // __${{self.c_ident}}_HH__
392''')
393
394        code.write(path, "%s.hh" % self.c_ident)
395
396    def printTypeCC(self, path):
397        code = self.symtab.codeFormatter()
398
399        code('''
400/** \\file ${{self.c_ident}}.cc
401 *
402 * Auto generated C++ code started by $__file__:$__line__
403 */
404
405#include <iostream>
406#include <memory>
407
408#include "mem/ruby/protocol/${{self.c_ident}}.hh"
409#include "mem/ruby/system/RubySystem.hh"
410
411using namespace std;
412''')
413
414        code('''
415/** \\brief Print the state of this object */
416void
417${{self.c_ident}}::print(ostream& out) const
418{
419    out << "[${{self.c_ident}}: ";
420''')
421
422        # For each field
423        code.indent()
424        for dm in self.data_members.values():
425            if dm.type.c_ident == "Addr":
426                code('''
427out << "${{dm.ident}} = " << printAddress(m_${{dm.ident}}) << " ";''')
428            else:
429                code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
430
431        code.dedent()
432
433        # Trailer
434        code('''
435    out << "]";
436}''')
437
438        # print the code for the methods in the type
439        for item in self.methods:
440            code(self.methods[item].generateCode())
441
442        code.write(path, "%s.cc" % self.c_ident)
443
444    def printEnumHH(self, path):
445        code = self.symtab.codeFormatter()
446        code('''
447/** \\file ${{self.c_ident}}.hh
448 *
449 * Auto generated C++ code started by $__file__:$__line__
450 */
451
452#ifndef __${{self.c_ident}}_HH__
453#define __${{self.c_ident}}_HH__
454
455#include <iostream>
456#include <string>
457
458''')
459        if self.isStateDecl:
460            code('#include "mem/ruby/protocol/AccessPermission.hh"')
461
462        if self.isMachineType:
463            code('#include <functional>')
464            code('#include "base/logging.hh"')
465            code('#include "mem/ruby/common/Address.hh"')
466            code('#include "mem/ruby/common/TypeDefines.hh"')
467            code('struct MachineID;')
468
469        code('''
470
471// Class definition
472/** \\enum ${{self.c_ident}}
473 *  \\brief ${{self.desc}}
474 */
475enum ${{self.c_ident}} {
476    ${{self.c_ident}}_FIRST,
477''')
478
479        code.indent()
480        # For each field
481        for i,(ident,enum) in enumerate(self.enums.iteritems()):
482            desc = enum.get("desc", "No description avaliable")
483            if i == 0:
484                init = ' = %s_FIRST' % self.c_ident
485            else:
486                init = ''
487            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
488        code.dedent()
489        code('''
490    ${{self.c_ident}}_NUM
491};
492
493// Code to convert from a string to the enumeration
494${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str);
495
496// Code to convert state to a string
497std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
498
499// Code to increment an enumeration type
500${{self.c_ident}} &operator++(${{self.c_ident}} &e);
501''')
502
503        if self.isMachineType:
504            code('''
505
506// define a hash function for the MachineType class
507namespace std {
508template<>
509struct hash<MachineType> {
510    std::size_t operator()(const MachineType &mtype) const {
511        return hash<size_t>()(static_cast<size_t>(mtype));
512    }
513};
514}
515
516''')
517        # MachineType hack used to set the base component id for each Machine
518        if self.isMachineType:
519            code('''
520int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
521MachineType ${{self.c_ident}}_from_base_level(int);
522int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
523int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
524''')
525
526            for enum in self.enums.itervalues():
527                code('''
528
529MachineID get${{enum.ident}}MachineID(NodeID RubyNode);
530''')
531
532        if self.isStateDecl:
533            code('''
534
535// Code to convert the current state to an access permission
536AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj);
537
538''')
539
540        # Trailer
541        code('''
542std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
543
544#endif // __${{self.c_ident}}_HH__
545''')
546
547        code.write(path, "%s.hh" % self.c_ident)
548
549    def printEnumCC(self, path):
550        code = self.symtab.codeFormatter()
551        code('''
552/** \\file ${{self.c_ident}}.hh
553 *
554 * Auto generated C++ code started by $__file__:$__line__
555 */
556
557#include <cassert>
558#include <iostream>
559#include <string>
560
561#include "base/logging.hh"
562#include "mem/ruby/protocol/${{self.c_ident}}.hh"
563
564using namespace std;
565
566''')
567
568        if self.isStateDecl:
569            code('''
570// Code to convert the current state to an access permission
571AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj)
572{
573    switch(obj) {
574''')
575            # For each case
576            code.indent()
577            for statePerm in self.statePermPairs:
578                code('  case ${{self.c_ident}}_${{statePerm[0]}}:')
579                code('    return AccessPermission_${{statePerm[1]}};')
580            code.dedent()
581            code ('''
582      default:
583        panic("Unknown state access permission converstion for ${{self.c_ident}}");
584    }
585}
586
587''')
588
589        if self.isMachineType:
590            for enum in self.enums.itervalues():
591                if enum.primary:
592                    code('#include "mem/ruby/protocol/${{enum.ident}}'
593                            '_Controller.hh"')
594            code('#include "mem/ruby/common/MachineID.hh"')
595
596        code('''
597// Code for output operator
598ostream&
599operator<<(ostream& out, const ${{self.c_ident}}& obj)
600{
601    out << ${{self.c_ident}}_to_string(obj);
602    out << flush;
603    return out;
604}
605
606// Code to convert state to a string
607string
608${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
609{
610    switch(obj) {
611''')
612
613        # For each field
614        code.indent()
615        for enum in self.enums.itervalues():
616            code('  case ${{self.c_ident}}_${{enum.ident}}:')
617            code('    return "${{enum.ident}}";')
618        code.dedent()
619
620        # Trailer
621        code('''
622      default:
623        panic("Invalid range for type ${{self.c_ident}}");
624    }
625}
626
627// Code to convert from a string to the enumeration
628${{self.c_ident}}
629string_to_${{self.c_ident}}(const string& str)
630{
631''')
632
633        # For each field
634        start = ""
635        code.indent()
636        for enum in self.enums.itervalues():
637            code('${start}if (str == "${{enum.ident}}") {')
638            code('    return ${{self.c_ident}}_${{enum.ident}};')
639            start = "} else "
640        code.dedent()
641
642        code('''
643    } else {
644        panic("Invalid string conversion for %s, type ${{self.c_ident}}", str);
645    }
646}
647
648// Code to increment an enumeration type
649${{self.c_ident}}&
650operator++(${{self.c_ident}}& e)
651{
652    assert(e < ${{self.c_ident}}_NUM);
653    return e = ${{self.c_ident}}(e+1);
654}
655''')
656
657        # MachineType hack used to set the base level and number of
658        # components for each Machine
659        if self.isMachineType:
660            code('''
661/** \\brief returns the base vector index for each machine type to be
662  * used by NetDest
663  *
664  * \\return the base vector index for each machine type to be used by NetDest
665  * \\see NetDest.hh
666  */
667int
668${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
669{
670    switch(obj) {
671''')
672
673            # For each field
674            code.indent()
675            for i,enum in enumerate(self.enums.itervalues()):
676                code('  case ${{self.c_ident}}_${{enum.ident}}:')
677                code('    return $i;')
678            code.dedent()
679
680            # total num
681            code('''
682      case ${{self.c_ident}}_NUM:
683        return ${{len(self.enums)}};
684
685      default:
686        panic("Invalid range for type ${{self.c_ident}}");
687    }
688}
689
690/** \\brief returns the machine type for each base vector index used by NetDest
691 *
692 * \\return the MachineType
693 */
694MachineType
695${{self.c_ident}}_from_base_level(int type)
696{
697    switch(type) {
698''')
699
700            # For each field
701            code.indent()
702            for i,enum in enumerate(self.enums.itervalues()):
703                code('  case $i:')
704                code('    return ${{self.c_ident}}_${{enum.ident}};')
705            code.dedent()
706
707            # Trailer
708            code('''
709      default:
710        panic("Invalid range for type ${{self.c_ident}}");
711    }
712}
713
714/** \\brief The return value indicates the number of components created
715 * before a particular machine\'s components
716 *
717 * \\return the base number of components for each machine
718 */
719int
720${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
721{
722    int base = 0;
723    switch(obj) {
724''')
725
726            # For each field
727            code.indent()
728            code('  case ${{self.c_ident}}_NUM:')
729            for enum in reversed(list(self.enums.values())):
730                # Check if there is a defined machine with this type
731                if enum.primary:
732                    code('    base += ${{enum.ident}}_Controller::getNumControllers();')
733                else:
734                    code('    base += 0;')
735                code('    M5_FALLTHROUGH;')
736                code('  case ${{self.c_ident}}_${{enum.ident}}:')
737            code('    break;')
738            code.dedent()
739
740            code('''
741      default:
742        panic("Invalid range for type ${{self.c_ident}}");
743    }
744
745    return base;
746}
747
748/** \\brief returns the total number of components for each machine
749 * \\return the total number of components for each machine
750 */
751int
752${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
753{
754    switch(obj) {
755''')
756
757            # For each field
758            for enum in self.enums.itervalues():
759                code('case ${{self.c_ident}}_${{enum.ident}}:')
760                if enum.primary:
761                    code('return ${{enum.ident}}_Controller::getNumControllers();')
762                else:
763                    code('return 0;')
764
765            # total num
766            code('''
767      case ${{self.c_ident}}_NUM:
768      default:
769        panic("Invalid range for type ${{self.c_ident}}");
770    }
771}
772''')
773
774            for enum in self.enums.itervalues():
775                code('''
776
777MachineID
778get${{enum.ident}}MachineID(NodeID RubyNode)
779{
780      MachineID mach = {MachineType_${{enum.ident}}, RubyNode};
781      return mach;
782}
783''')
784
785        # Write the file
786        code.write(path, "%s.cc" % self.c_ident)
787
788__all__ = [ "Type" ]
789