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