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