| # Copyright (c) 1999-2008 Mark D. Hill and David A. Wood |
| # Copyright (c) 2009 The Hewlett-Packard Development Company |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer; |
| # redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution; |
| # neither the name of the copyright holders nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| from collections import OrderedDict |
| |
| from slicc.util import PairContainer |
| from slicc.symbols.Symbol import Symbol |
| from slicc.symbols.Var import Var |
| |
| class DataMember(Var): |
| def __init__(self, symtab, ident, location, type, code, pairs, |
| machine, init_code): |
| super(DataMember, self).__init__(symtab, ident, location, type, |
| code, pairs, machine) |
| self.init_code = init_code |
| |
| class Enumeration(PairContainer): |
| def __init__(self, ident, pairs): |
| super(Enumeration, self).__init__(pairs) |
| self.ident = ident |
| self.primary = False |
| |
| class Type(Symbol): |
| def __init__(self, table, ident, location, pairs, machine=None): |
| super(Type, self).__init__(table, ident, location, pairs) |
| self.c_ident = ident |
| self.abstract_ident = "" |
| if machine: |
| if self.isExternal or self.isPrimitive: |
| if "external_name" in self: |
| self.c_ident = self["external_name"] |
| else: |
| # Append with machine name |
| self.c_ident = "%s_%s" % (machine, ident) |
| |
| self.pairs.setdefault("desc", "No description avaliable") |
| |
| # check for interface that this Type implements |
| if "interface" in self: |
| interface = self["interface"] |
| if interface in ("Message"): |
| self["message"] = "yes" |
| |
| # FIXME - all of the following id comparisons are fragile hacks |
| if self.ident in ("CacheMemory"): |
| self["cache"] = "yes" |
| |
| if self.ident in ("TBETable"): |
| self["tbe"] = "yes" |
| |
| if self.ident == "TimerTable": |
| self["timer"] = "yes" |
| |
| if self.ident == "DirectoryMemory": |
| self["dir"] = "yes" |
| |
| if self.ident == "PersistentTable": |
| self["persistent"] = "yes" |
| |
| if self.ident == "Prefetcher": |
| self["prefetcher"] = "yes" |
| |
| self.isMachineType = (ident == "MachineType") |
| |
| self.isStateDecl = ("state_decl" in self) |
| self.statePermPairs = [] |
| |
| self.data_members = OrderedDict() |
| self.methods = {} |
| self.enums = OrderedDict() |
| |
| @property |
| def isPrimitive(self): |
| return "primitive" in self |
| |
| @property |
| def isMessage(self): |
| return "message" in self |
| @property |
| def isBuffer(self): |
| return "buffer" in self |
| @property |
| def isInPort(self): |
| return "inport" in self |
| @property |
| def isOutPort(self): |
| return "outport" in self |
| @property |
| def isEnumeration(self): |
| return "enumeration" in self |
| @property |
| def isExternal(self): |
| return "external" in self |
| @property |
| def isGlobal(self): |
| return "global" in self |
| @property |
| def isInterface(self): |
| return "interface" in self |
| |
| # Return false on error |
| def addDataMember(self, ident, type, pairs, init_code): |
| if ident in self.data_members: |
| return False |
| |
| member = DataMember(self.symtab, ident, self.location, type, |
| "m_%s" % ident, pairs, None, init_code) |
| |
| self.data_members[ident] = member |
| self.symtab.registerSym(ident, member) |
| return True |
| |
| def dataMemberType(self, ident): |
| return self.data_members[ident].type |
| |
| def methodId(self, name, param_type_vec): |
| return '_'.join([name] + [ pt.c_ident for pt in param_type_vec ]) |
| |
| def methodIdAbstract(self, name, param_type_vec): |
| return '_'.join([name] + [ pt.abstract_ident for pt in param_type_vec ]) |
| |
| def statePermPairAdd(self, state_name, perm_name): |
| self.statePermPairs.append([state_name, perm_name]) |
| |
| def addFunc(self, func): |
| ident = self.methodId(func.ident, func.param_types) |
| if ident in self.methods: |
| return False |
| |
| self.methods[ident] = func |
| return True |
| |
| def addEnum(self, ident, pairs): |
| if ident in self.enums: |
| return False |
| |
| self.enums[ident] = Enumeration(ident, pairs) |
| |
| # Add default |
| if "default" not in self: |
| self["default"] = "%s_NUM" % self.c_ident |
| |
| return True |
| |
| ## Used to check if an enum has been already used and therefore |
| ## should not be used again. |
| def checkEnum(self, ident): |
| if ident in self.enums and not self.enums[ident].primary: |
| self.enums[ident].primary = True |
| return True |
| return False |
| |
| def writeCodeFiles(self, path, includes): |
| if self.isExternal: |
| # Do nothing |
| pass |
| elif self.isEnumeration: |
| self.printEnumHH(path) |
| self.printEnumCC(path) |
| else: |
| # User defined structs and messages |
| self.printTypeHH(path) |
| self.printTypeCC(path) |
| |
| def printTypeHH(self, path): |
| code = self.symtab.codeFormatter() |
| code(''' |
| /** \\file ${{self.c_ident}}.hh |
| * |
| * |
| * Auto generated C++ code started by $__file__:$__line__ |
| */ |
| |
| #ifndef __${{self.c_ident}}_HH__ |
| #define __${{self.c_ident}}_HH__ |
| |
| #include <iostream> |
| |
| #include "mem/ruby/slicc_interface/RubySlicc_Util.hh" |
| ''') |
| |
| for dm in self.data_members.values(): |
| if not dm.type.isPrimitive: |
| code('#include "mem/protocol/$0.hh"', dm.type.c_ident) |
| |
| parent = "" |
| if "interface" in self: |
| code('#include "mem/protocol/$0.hh"', self["interface"]) |
| parent = " : public %s" % self["interface"] |
| |
| code(''' |
| $klass ${{self.c_ident}}$parent |
| { |
| public: |
| ${{self.c_ident}} |
| ''', klass="class") |
| |
| if self.isMessage: |
| code('(Tick curTime) : %s(curTime) {' % self["interface"]) |
| else: |
| code('()\n\t\t{') |
| |
| code.indent() |
| if not self.isGlobal: |
| code.indent() |
| for dm in self.data_members.values(): |
| ident = dm.ident |
| if "default" in dm: |
| # look for default value |
| code('m_$ident = ${{dm["default"]}}; // default for this field') |
| elif "default" in dm.type: |
| # Look for the type default |
| tid = dm.type.c_ident |
| code('m_$ident = ${{dm.type["default"]}}; // default value of $tid') |
| else: |
| code('// m_$ident has no default') |
| code.dedent() |
| code('}') |
| |
| # ******** Copy constructor ******** |
| if not self.isGlobal: |
| code('${{self.c_ident}}(const ${{self.c_ident}}&other)') |
| |
| # Call superclass constructor |
| if "interface" in self: |
| code(' : ${{self["interface"]}}(other)') |
| |
| code('{') |
| code.indent() |
| |
| for dm in self.data_members.values(): |
| code('m_${{dm.ident}} = other.m_${{dm.ident}};') |
| |
| code.dedent() |
| code('}') |
| |
| # ******** Full init constructor ******** |
| if not self.isGlobal: |
| params = [ 'const %s& local_%s' % (dm.type.c_ident, dm.ident) \ |
| for dm in self.data_members.itervalues() ] |
| params = ', '.join(params) |
| |
| if self.isMessage: |
| params = "const Tick curTime, " + params |
| |
| code('${{self.c_ident}}($params)') |
| |
| # Call superclass constructor |
| if "interface" in self: |
| if self.isMessage: |
| code(' : ${{self["interface"]}}(curTime)') |
| else: |
| code(' : ${{self["interface"]}}()') |
| |
| code('{') |
| code.indent() |
| for dm in self.data_members.values(): |
| code('m_${{dm.ident}} = local_${{dm.ident}};') |
| |
| code.dedent() |
| code('}') |
| |
| # create a clone member |
| if self.isMessage: |
| code(''' |
| MsgPtr |
| clone() const |
| { |
| return std::shared_ptr<Message>(new ${{self.c_ident}}(*this)); |
| } |
| ''') |
| else: |
| code(''' |
| ${{self.c_ident}}* |
| clone() const |
| { |
| return new ${{self.c_ident}}(*this); |
| } |
| ''') |
| |
| if not self.isGlobal: |
| # const Get methods for each field |
| code('// Const accessors methods for each field') |
| for dm in self.data_members.values(): |
| code(''' |
| /** \\brief Const accessor method for ${{dm.ident}} field. |
| * \\return ${{dm.ident}} field |
| */ |
| const ${{dm.type.c_ident}}& |
| get${{dm.ident}}() const |
| { |
| return m_${{dm.ident}}; |
| } |
| ''') |
| |
| # Non-const Get methods for each field |
| code('// Non const Accessors methods for each field') |
| for dm in self.data_members.values(): |
| code(''' |
| /** \\brief Non-const accessor method for ${{dm.ident}} field. |
| * \\return ${{dm.ident}} field |
| */ |
| ${{dm.type.c_ident}}& |
| get${{dm.ident}}() |
| { |
| return m_${{dm.ident}}; |
| } |
| ''') |
| |
| #Set methods for each field |
| code('// Mutator methods for each field') |
| for dm in self.data_members.values(): |
| code(''' |
| /** \\brief Mutator method for ${{dm.ident}} field */ |
| void |
| set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}}) |
| { |
| m_${{dm.ident}} = local_${{dm.ident}}; |
| } |
| ''') |
| |
| code('void print(std::ostream& out) const;') |
| code.dedent() |
| code(' //private:') |
| code.indent() |
| |
| # Data members for each field |
| for dm in self.data_members.values(): |
| if "abstract" not in dm: |
| const = "" |
| init = "" |
| |
| # global structure |
| if self.isGlobal: |
| const = "static const " |
| |
| # init value |
| if dm.init_code: |
| # only global structure can have init value here |
| assert self.isGlobal |
| init = " = %s" % (dm.init_code) |
| |
| if "desc" in dm: |
| code('/** ${{dm["desc"]}} */') |
| |
| code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init;') |
| |
| # Prototypes for methods defined for the Type |
| for item in self.methods: |
| proto = self.methods[item].prototype |
| if proto: |
| code('$proto') |
| |
| code.dedent() |
| code('};') |
| |
| code(''' |
| inline std::ostream& |
| operator<<(std::ostream& out, const ${{self.c_ident}}& obj) |
| { |
| obj.print(out); |
| out << std::flush; |
| return out; |
| } |
| |
| #endif // __${{self.c_ident}}_HH__ |
| ''') |
| |
| code.write(path, "%s.hh" % self.c_ident) |
| |
| def printTypeCC(self, path): |
| code = self.symtab.codeFormatter() |
| |
| code(''' |
| /** \\file ${{self.c_ident}}.cc |
| * |
| * Auto generated C++ code started by $__file__:$__line__ |
| */ |
| |
| #include <iostream> |
| #include <memory> |
| |
| #include "mem/protocol/${{self.c_ident}}.hh" |
| #include "mem/ruby/system/RubySystem.hh" |
| |
| using namespace std; |
| ''') |
| |
| code(''' |
| /** \\brief Print the state of this object */ |
| void |
| ${{self.c_ident}}::print(ostream& out) const |
| { |
| out << "[${{self.c_ident}}: "; |
| ''') |
| |
| # For each field |
| code.indent() |
| for dm in self.data_members.values(): |
| if dm.type.c_ident == "Addr": |
| code(''' |
| out << "${{dm.ident}} = " << printAddress(m_${{dm.ident}}) << " ";''') |
| else: |
| code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''') |
| |
| code.dedent() |
| |
| # Trailer |
| code(''' |
| out << "]"; |
| }''') |
| |
| # print the code for the methods in the type |
| for item in self.methods: |
| code(self.methods[item].generateCode()) |
| |
| code.write(path, "%s.cc" % self.c_ident) |
| |
| def printEnumHH(self, path): |
| code = self.symtab.codeFormatter() |
| code(''' |
| /** \\file ${{self.c_ident}}.hh |
| * |
| * Auto generated C++ code started by $__file__:$__line__ |
| */ |
| |
| #ifndef __${{self.c_ident}}_HH__ |
| #define __${{self.c_ident}}_HH__ |
| |
| #include <iostream> |
| #include <string> |
| |
| ''') |
| if self.isStateDecl: |
| code('#include "mem/protocol/AccessPermission.hh"') |
| |
| if self.isMachineType: |
| code('#include <functional>') |
| code('#include "base/logging.hh"') |
| code('#include "mem/ruby/common/Address.hh"') |
| code('#include "mem/ruby/common/TypeDefines.hh"') |
| code('struct MachineID;') |
| |
| code(''' |
| |
| // Class definition |
| /** \\enum ${{self.c_ident}} |
| * \\brief ${{self.desc}} |
| */ |
| enum ${{self.c_ident}} { |
| ${{self.c_ident}}_FIRST, |
| ''') |
| |
| code.indent() |
| # For each field |
| for i,(ident,enum) in enumerate(self.enums.iteritems()): |
| desc = enum.get("desc", "No description avaliable") |
| if i == 0: |
| init = ' = %s_FIRST' % self.c_ident |
| else: |
| init = '' |
| code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */') |
| code.dedent() |
| code(''' |
| ${{self.c_ident}}_NUM |
| }; |
| |
| // Code to convert from a string to the enumeration |
| ${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str); |
| |
| // Code to convert state to a string |
| std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj); |
| |
| // Code to increment an enumeration type |
| ${{self.c_ident}} &operator++(${{self.c_ident}} &e); |
| ''') |
| |
| if self.isMachineType: |
| code(''' |
| |
| // define a hash function for the MachineType class |
| namespace std { |
| template<> |
| struct hash<MachineType> { |
| std::size_t operator()(const MachineType &mtype) const { |
| return hash<size_t>()(static_cast<size_t>(mtype)); |
| } |
| }; |
| } |
| |
| ''') |
| # MachineType hack used to set the base component id for each Machine |
| if self.isMachineType: |
| code(''' |
| int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj); |
| MachineType ${{self.c_ident}}_from_base_level(int); |
| int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj); |
| int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj); |
| ''') |
| |
| for enum in self.enums.itervalues(): |
| code(''' |
| |
| MachineID get${{enum.ident}}MachineID(NodeID RubyNode); |
| ''') |
| |
| if self.isStateDecl: |
| code(''' |
| |
| // Code to convert the current state to an access permission |
| AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj); |
| |
| ''') |
| |
| # Trailer |
| code(''' |
| std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj); |
| |
| #endif // __${{self.c_ident}}_HH__ |
| ''') |
| |
| code.write(path, "%s.hh" % self.c_ident) |
| |
| def printEnumCC(self, path): |
| code = self.symtab.codeFormatter() |
| code(''' |
| /** \\file ${{self.c_ident}}.hh |
| * |
| * Auto generated C++ code started by $__file__:$__line__ |
| */ |
| |
| #include <cassert> |
| #include <iostream> |
| #include <string> |
| |
| #include "base/logging.hh" |
| #include "mem/protocol/${{self.c_ident}}.hh" |
| |
| using namespace std; |
| |
| ''') |
| |
| if self.isStateDecl: |
| code(''' |
| // Code to convert the current state to an access permission |
| AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj) |
| { |
| switch(obj) { |
| ''') |
| # For each case |
| code.indent() |
| for statePerm in self.statePermPairs: |
| code(' case ${{self.c_ident}}_${{statePerm[0]}}:') |
| code(' return AccessPermission_${{statePerm[1]}};') |
| code.dedent() |
| code (''' |
| default: |
| panic("Unknown state access permission converstion for ${{self.c_ident}}"); |
| } |
| } |
| |
| ''') |
| |
| if self.isMachineType: |
| for enum in self.enums.itervalues(): |
| if enum.primary: |
| code('#include "mem/protocol/${{enum.ident}}_Controller.hh"') |
| code('#include "mem/ruby/common/MachineID.hh"') |
| |
| code(''' |
| // Code for output operator |
| ostream& |
| operator<<(ostream& out, const ${{self.c_ident}}& obj) |
| { |
| out << ${{self.c_ident}}_to_string(obj); |
| out << flush; |
| return out; |
| } |
| |
| // Code to convert state to a string |
| string |
| ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj) |
| { |
| switch(obj) { |
| ''') |
| |
| # For each field |
| code.indent() |
| for enum in self.enums.itervalues(): |
| code(' case ${{self.c_ident}}_${{enum.ident}}:') |
| code(' return "${{enum.ident}}";') |
| code.dedent() |
| |
| # Trailer |
| code(''' |
| default: |
| panic("Invalid range for type ${{self.c_ident}}"); |
| } |
| } |
| |
| // Code to convert from a string to the enumeration |
| ${{self.c_ident}} |
| string_to_${{self.c_ident}}(const string& str) |
| { |
| ''') |
| |
| # For each field |
| start = "" |
| code.indent() |
| for enum in self.enums.itervalues(): |
| code('${start}if (str == "${{enum.ident}}") {') |
| code(' return ${{self.c_ident}}_${{enum.ident}};') |
| start = "} else " |
| code.dedent() |
| |
| code(''' |
| } else { |
| panic("Invalid string conversion for %s, type ${{self.c_ident}}", str); |
| } |
| } |
| |
| // Code to increment an enumeration type |
| ${{self.c_ident}}& |
| operator++(${{self.c_ident}}& e) |
| { |
| assert(e < ${{self.c_ident}}_NUM); |
| return e = ${{self.c_ident}}(e+1); |
| } |
| ''') |
| |
| # MachineType hack used to set the base level and number of |
| # components for each Machine |
| if self.isMachineType: |
| code(''' |
| /** \\brief returns the base vector index for each machine type to be |
| * used by NetDest |
| * |
| * \\return the base vector index for each machine type to be used by NetDest |
| * \\see NetDest.hh |
| */ |
| int |
| ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj) |
| { |
| switch(obj) { |
| ''') |
| |
| # For each field |
| code.indent() |
| for i,enum in enumerate(self.enums.itervalues()): |
| code(' case ${{self.c_ident}}_${{enum.ident}}:') |
| code(' return $i;') |
| code.dedent() |
| |
| # total num |
| code(''' |
| case ${{self.c_ident}}_NUM: |
| return ${{len(self.enums)}}; |
| |
| default: |
| panic("Invalid range for type ${{self.c_ident}}"); |
| } |
| } |
| |
| /** \\brief returns the machine type for each base vector index used by NetDest |
| * |
| * \\return the MachineType |
| */ |
| MachineType |
| ${{self.c_ident}}_from_base_level(int type) |
| { |
| switch(type) { |
| ''') |
| |
| # For each field |
| code.indent() |
| for i,enum in enumerate(self.enums.itervalues()): |
| code(' case $i:') |
| code(' return ${{self.c_ident}}_${{enum.ident}};') |
| code.dedent() |
| |
| # Trailer |
| code(''' |
| default: |
| panic("Invalid range for type ${{self.c_ident}}"); |
| } |
| } |
| |
| /** \\brief The return value indicates the number of components created |
| * before a particular machine\'s components |
| * |
| * \\return the base number of components for each machine |
| */ |
| int |
| ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj) |
| { |
| int base = 0; |
| switch(obj) { |
| ''') |
| |
| # For each field |
| code.indent() |
| code(' case ${{self.c_ident}}_NUM:') |
| for enum in reversed(list(self.enums.values())): |
| # Check if there is a defined machine with this type |
| if enum.primary: |
| code(' base += ${{enum.ident}}_Controller::getNumControllers();') |
| else: |
| code(' base += 0;') |
| code(' M5_FALLTHROUGH;') |
| code(' case ${{self.c_ident}}_${{enum.ident}}:') |
| code(' break;') |
| code.dedent() |
| |
| code(''' |
| default: |
| panic("Invalid range for type ${{self.c_ident}}"); |
| } |
| |
| return base; |
| } |
| |
| /** \\brief returns the total number of components for each machine |
| * \\return the total number of components for each machine |
| */ |
| int |
| ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj) |
| { |
| switch(obj) { |
| ''') |
| |
| # For each field |
| for enum in self.enums.itervalues(): |
| code('case ${{self.c_ident}}_${{enum.ident}}:') |
| if enum.primary: |
| code('return ${{enum.ident}}_Controller::getNumControllers();') |
| else: |
| code('return 0;') |
| |
| # total num |
| code(''' |
| case ${{self.c_ident}}_NUM: |
| default: |
| panic("Invalid range for type ${{self.c_ident}}"); |
| } |
| } |
| ''') |
| |
| for enum in self.enums.itervalues(): |
| code(''' |
| |
| MachineID |
| get${{enum.ident}}MachineID(NodeID RubyNode) |
| { |
| MachineID mach = {MachineType_${{enum.ident}}, RubyNode}; |
| return mach; |
| } |
| ''') |
| |
| # Write the file |
| code.write(path, "%s.cc" % self.c_ident) |
| |
| __all__ = [ "Type" ] |