1 /* $Header: d:/cvsroot/tads/tads3/tcgen.h,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $ */ 2 3 /* 4 * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. 5 * 6 * Please see the accompanying license file, LICENSE.TXT, for information 7 * on using and copying this software. 8 */ 9 /* 10 Name 11 tcgen.h - TADS 3 Compiler code generator support classes 12 Function 13 14 Notes 15 16 Modified 17 05/08/99 MJRoberts - Creation 18 */ 19 20 #ifndef TCGEN_H 21 #define TCGEN_H 22 23 #include "t3std.h" 24 #include "os.h" 25 26 #include "tctargty.h" 27 28 /* ------------------------------------------------------------------------ */ 29 /* 30 * Stream ID's - an ID is associated with each data stream, and is used 31 * to identify the stream in an object file. These ID's are stored in 32 * object files, so any changes will render old object files obsolete 33 * and therefore require changing the object file signature's version 34 * number. 35 */ 36 37 /* the constant pool data stream */ 38 const char TCGEN_DATA_STREAM = 1; 39 40 /* the primary code pool data stream */ 41 const char TCGEN_CODE_STREAM = 2; 42 43 /* the static object data stream */ 44 const char TCGEN_OBJECT_STREAM = 3; 45 46 /* 47 * dictionary object data stream (not stored in an object file, because 48 * dictionaries aren't generated until link time) 49 */ 50 const char TCGEN_DICT_STREAM = 4; 51 52 /* 53 * grammar production object data stream (not stored in an object file, 54 * because grammar production objects aren't generated until link time) 55 */ 56 const char TCGEN_GRAMPROD_STREAM = 5; 57 58 /* BigNumber data stream */ 59 const char TCGEN_BIGNUM_STREAM = 6; 60 61 /* IntrinsicClass data stream */ 62 const char TCGEN_INTCLASS_STREAM = 7; 63 64 /* intrinsic class modifier data stream */ 65 const char TCGEN_ICMOD_STREAM = 8; 66 67 /* static initializer code stream */ 68 const char TCGEN_STATIC_CODE_STREAM = 9; 69 70 /* 71 * static initializer stream - contains a list of obj.prop identifiers 72 * for static initialization 73 */ 74 const char TCGEN_STATIC_INIT_ID_STREAM = 10; 75 76 77 /* ------------------------------------------------------------------------ */ 78 /* 79 * Data Stream. This object provides an in-memory byte stream. 80 * 81 * The data stream provides essentially a flat 32-bit address space, but 82 * dynamically allocates memory as needed, and works on 16-bit machines. 83 * The write pointer starts at offste zero, and is incremented by one 84 * for each byte written. 85 */ 86 87 /* 88 * size in bytes of each page - we'll use a size that will work in 89 * 16-bit architectures 90 */ 91 const size_t TCCS_PAGE_SIZE = 65000; 92 93 class CTcDataStream 94 { 95 public: 96 CTcDataStream(char stream_id); 97 virtual ~CTcDataStream(); 98 99 /* 100 * get my stream ID - this is assigned during stream creation and is 101 * used to identify the stream in an object file 102 */ get_stream_id()103 char get_stream_id() const { return stream_id_; } 104 105 /* 106 * Given a stream ID (TCGEN_xxx_STREAM), get the stream object. 107 * Logs an error if the stream ID is invalid. 108 */ 109 static CTcDataStream *get_stream_from_id(char stream_id, 110 const textchar_t *obj_fname); 111 112 /* get the current write pointer offset */ get_ofs()113 ulong get_ofs() const { return ofs_; } 114 115 /* 116 * decrement the write offset - this can be used to remove one or 117 * more bytes from the stream (for peephole optimization, for 118 * example) 119 */ 120 void dec_ofs(int amt); 121 122 /* 123 * Get a pointer to a block at a given offset and a given length. 124 * The block might not be returned contiguously, so we return how 125 * much data can be read at the returned pointer in 126 * (*available_len). If (*available_len) on return is less than 127 * requested_len, the caller must make a subsequent call, at (ofs + 128 * *available_len) of length (requested_len - *available_len), to 129 * read the next chunk; this process must be iterated until the 130 * entire request has been satisfied. 131 */ 132 const char *get_block_ptr(ulong ofs, 133 ulong requested_len, ulong *available_len); 134 135 /* extract a chunk of the stream into the given buffer */ 136 void copy_to_buf(char *buf, ulong start_ofs, ulong len); 137 138 /* 139 * Reserve space, advancing the write pointer without copying any 140 * data. The space written must eventually be written with 141 * write_at(), etc. Returns the offset of the start of the reserved 142 * space (which will always simply be the current offset before the 143 * call). 144 */ 145 ulong reserve(size_t len); 146 147 /* 148 * Append data from another stream. The old stream is destroyed by 149 * this operation - the data are physically moved from the old 150 * stream to the new stream. Fixup information is moved along with 151 * the stream data. 152 */ 153 void append_stream(CTcDataStream *stream); 154 155 /* write bytes to the stream */ write(char b)156 void write(char b) { write(&b, 1); } 157 void write(const char *buf, size_t len); 158 159 /* write bytes at an earlier offset */ 160 void write_at(ulong ofs, const char *buf, size_t len); write_at(ulong ofs,char b)161 void write_at(ulong ofs, char b) { write_at(ofs, &b, 1); } 162 163 /* write a TADS portable UINT2 value */ write2(uint val)164 void write2(uint val) 165 { 166 char buf[2]; 167 168 /* convert the value to UINT2 format and write it out */ 169 oswp2(buf, val); 170 write(buf, 2); 171 } 172 173 /* write a UINT2 at a given offset */ write2_at(ulong ofs,uint val)174 void write2_at(ulong ofs, uint val) 175 { 176 char buf[2]; 177 178 /* convert the value to UINT2 format and write it out */ 179 oswp2(buf, val); 180 write_at(ofs, buf, 2); 181 } 182 183 /* write a TADS portable UINT4 value */ write4(ulong val)184 void write4(ulong val) 185 { 186 char buf[4]; 187 188 /* convert the value to UINT4 format and write it out */ 189 oswp4(buf, val); 190 write(buf, 4); 191 } 192 193 /* write a TADS portable UINT4 value at a given offset */ write4_at(ulong ofs,ulong val)194 void write4_at(ulong ofs, ulong val) 195 { 196 char buf[4]; 197 198 /* convert the value to UINT4 format and write it out */ 199 oswp4(buf, val); 200 write_at(ofs, buf, 4); 201 } 202 203 /* 204 * Write an object ID at the current offset. If there's a global 205 * object ID fixup list, we'll add this reference to the list. 206 */ 207 void write_obj_id(ulong obj_id); 208 209 /* 210 * Write an object ID self-reference. This must be used when a 211 * modification of this object that assigns it a new object ID (not 212 * due to linking, but due to explicit replacement with the 'modify' 213 * statement) requires that this reference to the ID be changed to 214 * match the new object ID rather than referring to the replacement 215 * object. 216 */ 217 void write_obj_id_selfref(class CTcSymObj *obj_sym); 218 219 /* 220 * Write a property ID at the current offset. If there's a global 221 * property ID fixup list, we'll add this reference to the list. 222 */ 223 void write_prop_id(uint prop_id); 224 225 /* write an enum ID at the current offset, keeping fixups if needed */ 226 void write_enum_id(ulong enum_id); 227 228 /* get the byte at the given offset */ get_byte_at(ulong ofs)229 char get_byte_at(ulong ofs) 230 { 231 return *calc_addr(ofs); 232 } 233 234 /* read an INT2 value at the given offst */ read2_at(ulong ofs)235 int read2_at(ulong ofs) 236 { 237 char buf[2]; 238 239 /* read the two bytes */ 240 buf[0] = get_byte_at(ofs); 241 buf[1] = get_byte_at(ofs + 1); 242 return osrp2s(buf); 243 } 244 245 /* read a UINT2 value at the given offset */ readu2_at(ulong ofs)246 uint readu2_at(ulong ofs) 247 { 248 char buf[2]; 249 250 /* read the two bytes */ 251 buf[0] = get_byte_at(ofs); 252 buf[1] = get_byte_at(ofs + 1); 253 return osrp2(buf); 254 } 255 256 /* read an INT4 value at the given offset */ read4_at(ulong ofs)257 uint read4_at(ulong ofs) 258 { 259 char buf[4]; 260 261 /* read the four bytes */ 262 buf[0] = get_byte_at(ofs); 263 buf[1] = get_byte_at(ofs + 1); 264 buf[2] = get_byte_at(ofs + 2); 265 buf[3] = get_byte_at(ofs + 3); 266 return osrp4(buf); 267 } 268 269 /* 270 * Add an absolute fixup at the current stream location to a given 271 * absolute fixup list. We'll create a new fixup object, record our 272 * current offset in the fixup so that we come back and fix this 273 * location, and add the fixup object to the given list. 274 */ 275 void add_abs_fixup(struct CTcAbsFixup **list_head); 276 277 /* get the head of my list of anchors */ get_first_anchor()278 struct CTcStreamAnchor *get_first_anchor() 279 const { return first_anchor_; } 280 281 /* 282 * Add an anchor at the current offset. This should be used each 283 * time an atomic item is written to the stream. 284 * 285 * If the fixup list head pointer is not null, we will store fixups 286 * for the anchor in the given list. Otherwise, we'll provide an 287 * internal list head in the anchor object. Objects that are 288 * reachable through multiple references (such as an object 289 * associated with a symbol table entry) must typically keep their 290 * own fixup lists, because the fixup list might be needed before 291 * and after the data stream object is created. Objects that can 292 * only be reached through a single reference, which is created only 293 * when the data stream object is created, can use the internal 294 * fixup list instead. 295 * 296 * If an external fixup list is provided, the owning symbol table 297 * entry must be provided. This is necessary so that, when writing 298 * the anchor to an object file, we can also store a reference to 299 * the symbol that owns the list; this allows the association to be 300 * restored when the object file is loaded. 301 */ add_anchor(class CTcSymbol * owner_sym,struct CTcAbsFixup ** fixup_list_head)302 struct CTcStreamAnchor *add_anchor(class CTcSymbol *owner_sym, 303 struct CTcAbsFixup **fixup_list_head) 304 { 305 return add_anchor(owner_sym, fixup_list_head, get_ofs()); 306 } 307 308 /* add an anchor at the given offset */ 309 struct CTcStreamAnchor *add_anchor(class CTcSymbol *owner_sym, 310 struct CTcAbsFixup **fixup_list_head, 311 ulong ofs); 312 313 /* find an anchor at the given offset */ 314 struct CTcStreamAnchor *find_anchor(ulong ofs) const; 315 316 /* 317 * Write the stream to an object file. Writes the stream data and 318 * the list of anchors and their fixups. Every fixup should be 319 * reachable from an anchor, and all of the anchors are in our 320 * anchor list. 321 */ 322 void write_to_object_file(class CVmFile *fp); 323 324 /* 325 * Load an object file 326 */ 327 void load_object_file(class CVmFile *fp, const textchar_t *obj_fname); 328 329 /* 330 * Get/set the object file starting offset - this is the base offset 331 * of the stream for the current object. Because streams can refer 332 * to one another, the object file loader must set the starting 333 * offset for all streams before reading the first stream for an 334 * object file. The 'set' call simply sets the starting offset to 335 * the current offset; this won't be affected by subsequent loading 336 * into the stream, so we have a stable base address for the object 337 * file data in the stream. 338 */ get_object_file_start_ofs()339 ulong get_object_file_start_ofs() const { return obj_file_start_ofs_; } set_object_file_start_ofs()340 void set_object_file_start_ofs() { obj_file_start_ofs_ = ofs_; } 341 342 /* 343 * Reset - discard all data in the stream 344 */ 345 virtual void reset(); 346 347 protected: 348 /* allocate a new page */ 349 void alloc_page(); 350 351 /* 352 * Calculate the memory address of a given byte, given the byte's 353 * offset. The caller must be sure that the offset is less than the 354 * current write position, since this routine does not allocate new 355 * memory. 356 */ calc_addr(ulong ofs)357 char *calc_addr(ulong ofs) const 358 { 359 /* 360 * get the page number by dividing the offset by the page size; 361 * get the offset in the page from the remainder of the same 362 * division 363 */ 364 return pages_[ofs / TCCS_PAGE_SIZE] + (ofs % TCCS_PAGE_SIZE); 365 } 366 367 /* current write offset */ 368 ulong ofs_; 369 370 /* start offset of the stream for the current object file's data */ 371 ulong obj_file_start_ofs_; 372 373 /* current write pointer */ 374 char *wp_; 375 376 /* space remaining on the current page */ 377 size_t rem_; 378 379 /* current page */ 380 size_t page_cur_; 381 382 /* array of pages */ 383 char **pages_; 384 385 /* number of page slots */ 386 size_t page_slots_; 387 388 /* number of pages allocated */ 389 size_t page_cnt_; 390 391 /* head and tail of my list of anchors */ 392 struct CTcStreamAnchor *first_anchor_; 393 struct CTcStreamAnchor *last_anchor_; 394 395 /* 396 * parser memory allocator - we use this for allocating label and 397 * fixup objects; these objects fit the characteristics used for the 398 * parser memory allocator, so we can get better memory management 399 * efficiency by using this allocator 400 */ 401 class CTcPrsMem *allocator_; 402 403 /* my stream ID, for identification in the object file */ 404 char stream_id_; 405 }; 406 407 /* ------------------------------------------------------------------------ */ 408 /* 409 * Debugging line record. This record stores information on one 410 * executable line of source code, associating the byte-code location of 411 * the code with the source file ID and line number, plus the frame 412 * identifier (which gives the local scope in effect for the line of 413 * code). 414 */ 415 struct tcgen_line_t 416 { 417 /* 418 * Byte-code offset of the first instruction for this source line. 419 * This is expressed as an offset from the start of the method 420 * header, which means that this value is relative and thus doesn't 421 * need any adjustment for linking or any other changes to the 422 * absolute location where the code is stored. 423 */ 424 uint ofs; 425 426 /* 427 * Source file ID and line number. The source file ID is the index 428 * of the file descriptor in the tokenizer's master source file 429 * list, so it must be adjusted when multiple object files are 430 * linked together for the fact that the file descriptor indices can 431 * change. 432 */ 433 int source_id; 434 long source_line; 435 436 /* 437 * frame - this is the local scope frame for this line, which 438 * specifies the local symbol table in effect for the line of code 439 */ 440 class CTcPrsSymtab *frame; 441 }; 442 443 /* number of line records per page */ 444 const size_t TCGEN_LINE_PAGE_SIZE = 1024; 445 446 /* 447 * Debugging line record page. We allocate blocks of line records for 448 * memory efficiency; this is a page of line numbers. 449 */ 450 struct tcgen_line_page_t 451 { 452 /* underlying line records */ 453 tcgen_line_t lines[TCGEN_LINE_PAGE_SIZE]; 454 }; 455 456 457 /* ------------------------------------------------------------------------ */ 458 /* 459 * Code Stream. This object provides a place for code generators to 460 * store byte code. A code stream is an extension of a byte stream, 461 * with support for symbol table access; enclosing switch, loop, and 462 * 'try' block tracking; forward-reference labels; and relative jump 463 * generation. 464 */ 465 466 /* code stream class */ 467 class CTcCodeStream: public CTcDataStream 468 { 469 public: 470 CTcCodeStream(char stream_id); 471 ~CTcCodeStream(); 472 473 /* 474 * Temporary label operations. A temporary label can be used to 475 * generate forward or reverse jumps. 476 */ 477 478 /* allocate a new label at the current write offset */ 479 struct CTcCodeLabel *new_label_here(); 480 481 /* 482 * allocate a forward-reference label; the position will be defined 483 * later via the def_label_pos() method 484 */ 485 struct CTcCodeLabel *new_label_fwd(); 486 487 /* 488 * define the position of a label allocated as a forward reference 489 * to be the current write offset 490 */ 491 void def_label_pos(struct CTcCodeLabel *lbl); 492 493 /* 494 * Remove a fixup at a particular location for a label. This can be 495 * used if an instruction is being removed (for optimization, for 496 * example). 497 */ 498 void remove_fixup_at_ofs(struct CTcCodeLabel *lbl, ulong ofs); 499 500 /* 501 * Check a label's fixup list to see if it has a fixup at a 502 * particular code stream offset. Returns true if there is such a 503 * fixup, false if not. 504 */ 505 int has_fixup_at_ofs(struct CTcCodeLabel *lbl, ulong ofs); 506 507 /* 508 * Release all active labels. Logs an internal error if any labels 509 * are still forward-declared. 510 */ 511 void release_labels(); 512 513 /* 514 * Get the current enclosing statement. This is used to find the 515 * target of a 'break' or 'continue', and is also used to invoke 516 * 'finally' blocks when leaving a nested block. 517 */ get_enclosing()518 class CTPNStmEnclosing *get_enclosing() const { return enclosing_; } 519 520 /* 521 * Set the enclosing statement. During code generation, each time 522 * an enclosing statement is encountered, it should be set as the 523 * current enclosing statement for the duration of its code 524 * generation, so that its subnodes can find it. This routine 525 * returns the previous enclosing statement so that it can be 526 * restored later. 527 */ set_enclosing(CTPNStmEnclosing * stm)528 class CTPNStmEnclosing *set_enclosing(CTPNStmEnclosing *stm) 529 { 530 CTPNStmEnclosing *old_stm; 531 532 /* save the old one */ 533 old_stm = enclosing_; 534 535 /* set the new one */ 536 enclosing_ = stm; 537 538 /* return the old one */ 539 return old_stm; 540 } 541 542 /* 543 * Set the current code body 544 */ set_code_body(CTPNCodeBody * cb)545 class CTPNCodeBody *set_code_body(CTPNCodeBody *cb) 546 { 547 CTPNCodeBody *old_cb; 548 549 /* save the old one */ 550 old_cb = code_body_; 551 552 /* set the new one */ 553 code_body_ = cb; 554 555 /* return the old one */ 556 return old_cb; 557 } 558 559 /* get the current code body being generated */ get_code_body()560 class CTPNCodeBody *get_code_body() const { return code_body_; } 561 562 /* 563 * Write the relative offset to the given label as a 2- or 4-byte 564 * value in TADS portable INT2 or INT4 format. The 'bias' value 565 * gives the offset from the current write pointer of the source 566 * position; the relative value written is thus: 567 * 568 * (label - (current + bias)) 569 */ write_ofs2(struct CTcCodeLabel * lbl,int bias)570 void write_ofs2(struct CTcCodeLabel *lbl, int bias) 571 { write_ofs(lbl, bias, FALSE); } write_ofs4(struct CTcCodeLabel * lbl,int bias)572 void write_ofs4(struct CTcCodeLabel *lbl, int bias) 573 { write_ofs(lbl, bias, TRUE); } 574 575 /* 576 * Set the enclosing "switch" statement node, returning the previous 577 * one to allow for later restoration 578 */ set_switch(class CTPNStmSwitch * sw)579 class CTPNStmSwitch *set_switch(class CTPNStmSwitch *sw) 580 { 581 class CTPNStmSwitch *old_sw; 582 583 /* remember the current switch node for a moment */ 584 old_sw = cur_switch_; 585 586 /* set the new switch */ 587 cur_switch_ = sw; 588 589 /* return the previous one */ 590 return old_sw; 591 } 592 593 /* get the current switch */ get_switch()594 class CTPNStmSwitch *get_switch() const { return cur_switch_; } 595 596 /* get the active symbol table */ get_symtab()597 class CTcPrsSymtab *get_symtab() const { return symtab_; } 598 599 /* get the active 'goto' symbol table */ get_goto_symtab()600 class CTcPrsSymtab *get_goto_symtab() const { return goto_symtab_; } 601 602 /* set the active symbol table */ set_symtab(class CTcPrsSymtab * symtab)603 void set_symtab(class CTcPrsSymtab *symtab) { symtab_ = symtab; } 604 605 /* set the active 'goto' symbol table */ set_goto_symtab(class CTcPrsSymtab * symtab)606 void set_goto_symtab(class CTcPrsSymtab *symtab) 607 { goto_symtab_ = symtab; } 608 609 /* 610 * get/self 'self' availability - if we're generating code for a 611 * stand-alone function, 'self' is not available; if we're 612 * generating code for an object method, 'self' is available 613 */ is_self_available()614 int is_self_available() const { return self_available_; } set_self_available(int f)615 void set_self_available(int f) { self_available_ = f; } 616 617 /* add a debugging line record at the current byte-code offset */ 618 void add_line_rec(class CTcTokFileDesc *file, long linenum); 619 620 /* clear all line records */ clear_line_recs()621 void clear_line_recs() { line_cnt_ = 0; } 622 623 /* get the number of line records */ get_line_rec_count()624 size_t get_line_rec_count() const { return line_cnt_; } 625 626 /* get the nth line record */ 627 struct tcgen_line_t *get_line_rec(size_t n); 628 629 /* set the starting offset of the current method */ set_method_ofs(ulong ofs)630 void set_method_ofs(ulong ofs) { method_ofs_ = ofs; } 631 632 /* clear the list of local frames in the current method */ clear_local_frames()633 void clear_local_frames() 634 { 635 /* clear the list */ 636 frame_head_ = frame_tail_ = 0; 637 638 /* reset the count */ 639 frame_cnt_ = 0; 640 } 641 642 /* 643 * Set the current local frame. This will add the frame to the 644 * master list of frames for the current method if it's not already 645 * there, and will establish the frame as the current local frame. 646 * Returns the previous local frame, so that the caller can restore 647 * the enclosing frame when leaving a nested frame. 648 */ set_local_frame(class CTcPrsSymtab * symtab)649 class CTcPrsSymtab *set_local_frame(class CTcPrsSymtab *symtab) 650 { 651 class CTcPrsSymtab *old_frame; 652 653 /* remember the original local frame, so we can return it later */ 654 old_frame = cur_frame_; 655 656 /* remember the current frame */ 657 cur_frame_ = symtab; 658 659 /* add it to the local frame list for the method if necessary */ 660 add_local_frame(symtab); 661 662 /* return the original local frame */ 663 return old_frame; 664 } 665 666 /* get the local frame count for this method */ get_frame_count()667 size_t get_frame_count() const { return frame_cnt_; } 668 669 /* get the head of the frame list for the method */ get_first_frame()670 class CTcPrsSymtab *get_first_frame() const { return frame_head_; } 671 672 /* reset */ 673 virtual void reset(); 674 675 protected: 676 /* 677 * add a frame to the list of local frames; does nothing if the 678 * frame is already in the list for this method 679 */ 680 void add_local_frame(class CTcPrsSymtab *symtab); 681 682 /* allocate additional line record pages */ 683 void alloc_line_pages(size_t number_to_add); 684 685 /* allocate a label object */ 686 struct CTcCodeLabel *alloc_label(); 687 688 /* allocate a fixup object */ 689 struct CTcLabelFixup *alloc_fixup(); 690 691 /* 692 * Write an offset to the given label. If is_long is true, we'll 693 * write an INT4 offset; otherwise we'll write an INT2 offset value. 694 */ 695 void write_ofs(struct CTcCodeLabel *lbl, int bias, int is_long); 696 697 /* head of list of active temporary labels */ 698 struct CTcCodeLabel *active_lbl_; 699 700 /* head of list of free temporary label objects */ 701 struct CTcCodeLabel *free_lbl_; 702 703 /* head of list of free label fixup objects */ 704 struct CTcLabelFixup *free_fixup_; 705 706 /* current symbol table */ 707 class CTcPrsSymtab *symtab_; 708 709 /* current 'goto' symbol table */ 710 class CTcPrsSymtab *goto_symtab_; 711 712 /* current "switch" statement */ 713 class CTPNStmSwitch *cur_switch_; 714 715 /* current enclosing statement */ 716 class CTPNStmEnclosing *enclosing_; 717 718 /* current code body being generated */ 719 class CTPNCodeBody *code_body_; 720 721 /* flag: 'self' is available in current code body */ 722 unsigned int self_available_ : 1; 723 724 /* array of line record pages */ 725 tcgen_line_page_t **line_pages_; 726 size_t line_pages_alloc_; 727 728 /* number of line records actually used */ 729 size_t line_cnt_; 730 731 /* starting offst of current method */ 732 ulong method_ofs_; 733 734 /* head and tail of list of local frames for the current method */ 735 class CTcPrsSymtab *frame_head_; 736 class CTcPrsSymtab *frame_tail_; 737 738 /* number of frames in the local method list so far */ 739 size_t frame_cnt_; 740 741 /* currently active local frame */ 742 class CTcPrsSymtab *cur_frame_; 743 }; 744 745 /* ------------------------------------------------------------------------ */ 746 /* 747 * Code Stream Parser-Allocated Object - this is a base class for 748 * objects allocated by a CTcPrsMem allocator. 749 */ 750 struct CTcCSPrsAllocObj 751 { 752 /* allocate via the parser allocator */ 753 void *operator new(size_t siz, class CTcPrsMem *allocator); 754 }; 755 756 /* ------------------------------------------------------------------------ */ 757 /* 758 * Code label 759 */ 760 struct CTcCodeLabel: CTcCSPrsAllocObj 761 { CTcCodeLabelCTcCodeLabel762 CTcCodeLabel() 763 { 764 /* clear members */ 765 nxt = 0; 766 ofs = 0; 767 fhead = 0; 768 is_known = FALSE; 769 } 770 771 /* next code label */ 772 CTcCodeLabel *nxt; 773 774 /* offset of the code associated with the label */ 775 ulong ofs; 776 777 /* 778 * head of list of fixups for this label; this will always be null 779 * once the label's offset is known, since we will resolve any 780 * existing fixups as soon as the label is defined and will add no 781 * further fixups after it's known, since we can generate correct 782 * offsets directly 783 */ 784 struct CTcLabelFixup *fhead; 785 786 /* flag: true -> offset is known */ 787 uint is_known : 1; 788 }; 789 790 /* 791 * Code label fixup. Each time we generate a jump to a code label that 792 * hasn't been defined yet, we'll generate a fixup and attach it to the 793 * label. The fixup records the location of the jump; as soon as the 794 * label is defined, we'll go through all of the fixup records 795 * associated with the label, and fill in the correct jump offset. 796 */ 797 struct CTcLabelFixup: CTcCSPrsAllocObj 798 { CTcLabelFixupCTcLabelFixup799 CTcLabelFixup() 800 { 801 /* clear members */ 802 nxt = 0; 803 ofs = 0; 804 bias = 0; 805 is_long = FALSE; 806 } 807 808 /* next fixup in same list */ 809 CTcLabelFixup *nxt; 810 811 /* code offset of the jump in need of fixing */ 812 ulong ofs; 813 814 /* 815 * bias to apply to jump offset (the value we will write is: 816 * 817 * (target - (ofs + bias)) 818 */ 819 int bias; 820 821 /* long jump - true -> INT4 offset, false -> INT2 offset */ 822 uint is_long : 1; 823 }; 824 825 826 /* ------------------------------------------------------------------------ */ 827 /* 828 * Absolute Fixup. Each time we generate a reference to an offset in a 829 * stream, we must save the location so that we can go back and fix up 830 * the offset after the final image file layout configuration. We can't 831 * know until we've generated the entire program what the final location 832 * of any stream is, because we won't know how large we're going to make 833 * the pool pages until we've generated the whole program. 834 * 835 * Each absolute fixup is stored in a list anchored in the object that 836 * owns the object in the stream to which the fixup refers. During the 837 * image layout configuration phase, we go through the list of all pool 838 * blocks, and figure out where the pool block will go in the image 839 * file; once this is known, we apply all of the fixups for that 840 * block by scanning the block's fixup list. 841 */ 842 struct CTcAbsFixup: CTcCSPrsAllocObj 843 { 844 /* next fixup in same list */ 845 CTcAbsFixup *nxt; 846 847 /* stream containing the reference */ 848 class CTcDataStream *ds; 849 850 /* location in stream 'ds' of the 4-byte pointer value to fix */ 851 ulong ofs; 852 853 /* 854 * Add an absolute fixup, at a given offset in a given stream, to a 855 * given list. 856 */ 857 static void add_abs_fixup(struct CTcAbsFixup **list_head, 858 CTcDataStream *ds, ulong ofs); 859 860 /* 861 * Fix up a fix-up list, given the final address of the referenced 862 * object. Scans the fix-up list and stores the final address at 863 * each location in the list. 864 */ 865 static void fix_abs_fixup(struct CTcAbsFixup *list_head, 866 ulong final_ofs); 867 868 /* write a fixup list to an object file */ 869 static void write_fixup_list_to_object_file(class CVmFile *fp, 870 CTcAbsFixup *list_head); 871 872 /* load a fixup list from an object file */ 873 static void 874 load_fixup_list_from_object_file(class CVmFile *fp, 875 const textchar_t *obj_fname, 876 CTcAbsFixup **list_head); 877 878 }; 879 880 /* ------------------------------------------------------------------------ */ 881 /* 882 * ID Fixup. We use this to track a reference to an object ID or 883 * property ID. These fixups are needed when combining object files 884 * that were compiled separately, since we must reconcile the ID name 885 * spaces of the multiple files. 886 */ 887 888 /* translation datatypes */ 889 enum tcgen_xlat_type 890 { 891 /* object ID's - translation table type is (tctarg_obj_id_t *) */ 892 TCGEN_XLAT_OBJ, 893 894 /* property ID's - translation table type is (tctarg_prop_id_t *) */ 895 TCGEN_XLAT_PROP, 896 897 /* enum's - translation table type is (ulong *) */ 898 TCGEN_XLAT_ENUM 899 }; 900 901 /* 902 * ID fixup class 903 */ 904 struct CTcIdFixup: CTcCSPrsAllocObj 905 { 906 /* initialize */ CTcIdFixupCTcIdFixup907 CTcIdFixup(class CTcDataStream *ds, ulong ofs, ulong id) 908 { 909 /* remember the data */ 910 ds_ = ds; 911 ofs_ = ofs; 912 id_ = id; 913 914 /* we're not in a list yet */ 915 nxt_ = 0; 916 } 917 918 /* add a fixup to a fixup list */ 919 static void add_fixup(CTcIdFixup **list_head, class CTcDataStream *ds, 920 ulong ofs, ulong id); 921 922 /* write a fixup list to an object file */ 923 static void write_to_object_file(class CVmFile *fp, CTcIdFixup *head); 924 925 /* 926 * Load an ID fixup list from an object file. 927 * 928 * 'xlat' is the translation array; it consists of elements of the 929 * appropriate type, depending on xlat_type. 930 * 931 * 'xlat_cnt' is the number of elements in the 'xlat' array. 932 * 933 * 'stream_element_size' is the size of the stream data elements 934 * that we're fixing up. This can be 2 for a UINT2 value, or 4 for 935 * a UINT4 value. 936 * 937 * 'fname' is the object filename, which we need for reporting 938 * errors that we encounter in the fixup data. 939 * 940 * If 'fixup_list_head' is provided, it points to the head of a list 941 * that we use to store new fixups. For each fixup we read, we 942 * create a new fixup referring to the same element. This is needed 943 * if the file we're reading will be turned back into another object 944 * file at some point and needs relative object ID information to be 945 * stored. 946 */ 947 static void load_object_file(class CVmFile *fp, 948 const void *xlat, ulong xlat_cnt, 949 tcgen_xlat_type xlat_type, 950 size_t stream_element_size, 951 const textchar_t *fname, 952 CTcIdFixup **fixup_list_head); 953 954 /* 955 * apply the fixup, given the final ID to use, and the size of the 956 * data item to write (2 for a UINT2, 4 for a UINT4) 957 */ 958 void apply_fixup(ulong final_id, size_t siz); 959 960 /* the stream containing the reference to this object */ 961 class CTcDataStream *ds_; 962 963 /* the offset in the stream of the reference */ 964 ulong ofs_; 965 966 /* 967 * the local ID - this is the ID that is used locally in the 968 * separate file before it's translated to the final value global to 969 * the combined files 970 */ 971 ulong id_; 972 973 /* next fixup in the list */ 974 CTcIdFixup *nxt_; 975 }; 976 977 978 /* ------------------------------------------------------------------------ */ 979 /* 980 * Data Stream Anchor. Each time we add an object to a data stream, we 981 * must add an anchor to the stream. The anchor tracks the indivisible 982 * object and all references (via a fixup list) to the object. 983 */ 984 struct CTcStreamAnchor: CTcCSPrsAllocObj 985 { CTcStreamAnchorCTcStreamAnchor986 CTcStreamAnchor(class CTcSymbol *fixup_owner_sym, 987 struct CTcAbsFixup **fixup_list_head, ulong stream_ofs) 988 { 989 /* we're not in a list yet */ 990 nxt_ = 0; 991 992 /* 993 * if the caller provided an external fixup list head pointer, 994 * use it; otherwise, use our internal fixup list 995 */ 996 if (fixup_list_head != 0) 997 { 998 /* use the external fixup list */ 999 fixup_list_head_ = fixup_list_head; 1000 1001 /* remember the owning symbol */ 1002 fixup_info_.fixup_owner_sym_ = fixup_owner_sym; 1003 } 1004 else 1005 { 1006 /* use our internal fixup list */ 1007 fixup_list_head_ = &fixup_info_.internal_fixup_head_; 1008 1009 /* we have no fixup items in our internal list yet */ 1010 fixup_info_.internal_fixup_head_ = 0; 1011 } 1012 1013 /* remember the stream offset */ 1014 ofs_ = stream_ofs; 1015 1016 /* our pool address is not yet known */ 1017 addr_ = 0; 1018 1019 /* we're not yet replaced */ 1020 replaced_ = FALSE; 1021 } 1022 1023 /* 1024 * Detach from our symbol, switching to our internal fixup list. We 1025 * leave any fixups that were previously associated with our symbol 1026 * with the symbol, so this detaches us from any fixups currently 1027 * pointing to me. This is useful for replacing and modifying symbols, 1028 * since it allows the anchor for the original definition to be 1029 * dissociated from the symbol, so that the symbol can be reused with a 1030 * different anchor. 1031 */ detach_from_symbolCTcStreamAnchor1032 void detach_from_symbol() 1033 { 1034 /* switch to our internal fixup list */ 1035 fixup_list_head_ = &fixup_info_.internal_fixup_head_; 1036 1037 /* we have nothing in our list yet */ 1038 fixup_info_.internal_fixup_head_ = 0; 1039 } 1040 1041 /* get my offset - this is the stream offset, not the final address */ get_ofsCTcStreamAnchor1042 ulong get_ofs() const { return ofs_; } 1043 1044 /* 1045 * Get my final address - valid only after the linking phase (as 1046 * defined and performed by the target-specific code generator). 1047 * The exact meaning of this address is defined by the 1048 * target-specific code generator, and this value is meant for use 1049 * exclusively by the target code generator; the target-independent 1050 * part of the compiler should have no use for this value. 1051 */ get_addrCTcStreamAnchor1052 ulong get_addr() const { return addr_; } 1053 1054 /* 1055 * Set the address - this should be invoked by the target-specific 1056 * code generator during the link phase when the final address of 1057 * the stream object is known. This will store the address in the 1058 * anchor, and will apply all of the outstanding fixups for the 1059 * object. 1060 */ 1061 void set_addr(ulong addr); 1062 1063 /* 1064 * Get the length. We don't separately track the length, because 1065 * the length can be deduced from the offset of the next item (or 1066 * from the length of the stream, when the item is the last item in 1067 * the stream). 1068 */ 1069 ulong get_len(class CTcDataStream *ds) const; 1070 1071 /* 1072 * Get the symbol table entry that owns my fixup list, if the fixup 1073 * list is external. Returns null if the fixup list is internal. 1074 */ get_fixup_owner_symCTcStreamAnchor1075 class CTcSymbol *get_fixup_owner_sym() const 1076 { 1077 return (fixup_list_head_ == &fixup_info_.internal_fixup_head_ 1078 ? 0 : fixup_info_.fixup_owner_sym_); 1079 } 1080 1081 /* get/set the 'replaced' flag */ is_replacedCTcStreamAnchor1082 int is_replaced() const { return replaced_; } set_replacedCTcStreamAnchor1083 void set_replaced(int f) { replaced_ = f; } 1084 1085 /* next anchor in same list */ 1086 CTcStreamAnchor *nxt_; 1087 1088 /* 1089 * Pointer to fixup list head for the object - this is a list of the 1090 * fixups for references to this object. Because an object might 1091 * have a fixup list before the object is added to the data stream 1092 * (anything that is reachable through multiple references, such as 1093 * a function in a symbol table, could have such a fixup list), 1094 * we'll set this to refer to the external fixup list. Otherwise, 1095 * we'll set it to point to our own internal fixup list head. 1096 */ 1097 struct CTcAbsFixup **fixup_list_head_; 1098 1099 /* 1100 * If we have an external fixup list, we need to store the symbol 1101 * table entry that owns the fixup list; otherwise, we must store 1102 * the fixup list head. Since we only need one or the other, 1103 * compress the storage into a union. 1104 */ 1105 union 1106 { 1107 /* 1108 * internal fixup list - this is used if no external fixup list 1109 * exists for the object 1110 */ 1111 struct CTcAbsFixup *internal_fixup_head_; 1112 1113 /* owning symbol table entry, if the fixup list is external */ 1114 class CTcSymbol *fixup_owner_sym_; 1115 } fixup_info_; 1116 1117 /* offset of this object in the stream */ 1118 ulong ofs_; 1119 1120 /* 1121 * final absolute address - this won't be known until the layout of 1122 * the pool or data segment containing this object is calculated, 1123 * which happens during the link phase 1124 */ 1125 ulong addr_; 1126 1127 /* 1128 * Flag: this code block has been replaced by another one. This is 1129 * set when the function or property that owns this code is replaced 1130 * by another implementation; when this is set, the code block 1131 * should not be written to the image file. 1132 */ 1133 unsigned int replaced_ : 1; 1134 }; 1135 1136 #endif /* TCGEN_H */ 1137 1138