| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header: d:/cvsroot/tads/tads3/tcgen.cpp,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | tcgen.cpp - TADS 3 Compiler code generator support classes |
| | 15 | Function |
| | 16 | |
| | 17 | Notes |
| | 18 | |
| | 19 | Modified |
| | 20 | 05/09/99 MJRoberts - Creation |
| | 21 | */ |
| | 22 | |
| | 23 | #include <stdlib.h> |
| | 24 | #include <string.h> |
| | 25 | |
| | 26 | #include "t3std.h" |
| | 27 | #include "os.h" |
| | 28 | #include "tcglob.h" |
| | 29 | #include "tcgen.h" |
| | 30 | #include "vmerr.h" |
| | 31 | #include "tcerrnum.h" |
| | 32 | #include "tctok.h" |
| | 33 | #include "tcprs.h" |
| | 34 | #include "tcmain.h" |
| | 35 | #include "vmfile.h" |
| | 36 | #include "tctarg.h" |
| | 37 | |
| | 38 | |
| | 39 | /* ------------------------------------------------------------------------ */ |
| | 40 | /* |
| | 41 | * Data/Code Stream Parser-Allocated Object |
| | 42 | */ |
| | 43 | |
| | 44 | /* |
| | 45 | * allocate via a parser memory allocator |
| | 46 | */ |
| | 47 | void *CTcCSPrsAllocObj::operator new(size_t siz, CTcPrsMem *allocator) |
| | 48 | { |
| | 49 | /* allocate via the allocator */ |
| | 50 | return allocator->alloc(siz); |
| | 51 | } |
| | 52 | |
| | 53 | /* ------------------------------------------------------------------------ */ |
| | 54 | /* |
| | 55 | * Data Stream |
| | 56 | */ |
| | 57 | |
| | 58 | /* |
| | 59 | * initialize |
| | 60 | */ |
| | 61 | CTcDataStream::CTcDataStream(char stream_id) |
| | 62 | { |
| | 63 | /* remember my ID */ |
| | 64 | stream_id_ = stream_id; |
| | 65 | |
| | 66 | /* nothing is allocated yet */ |
| | 67 | ofs_ = 0; |
| | 68 | obj_file_start_ofs_ = 0; |
| | 69 | pages_ = 0; |
| | 70 | page_slots_ = 0; |
| | 71 | page_cnt_ = 0; |
| | 72 | page_cur_ = 0; |
| | 73 | rem_ = 0; |
| | 74 | wp_ = 0; |
| | 75 | |
| | 76 | /* we have no anchors yet */ |
| | 77 | first_anchor_ = last_anchor_ = 0; |
| | 78 | |
| | 79 | /* create our parser memory allocator */ |
| | 80 | allocator_ = new CTcPrsMem(); |
| | 81 | } |
| | 82 | |
| | 83 | /* |
| | 84 | * delete |
| | 85 | */ |
| | 86 | CTcDataStream::~CTcDataStream() |
| | 87 | { |
| | 88 | size_t i; |
| | 89 | |
| | 90 | /* delete the page slots if we allocated any */ |
| | 91 | for (i = 0 ; i < page_cnt_ ; ++i) |
| | 92 | t3free(pages_[i]); |
| | 93 | |
| | 94 | /* delete the page slot array if we allocated it */ |
| | 95 | if (pages_ != 0) |
| | 96 | t3free(pages_); |
| | 97 | |
| | 98 | /* delete our label/fixup allocator */ |
| | 99 | delete allocator_; |
| | 100 | } |
| | 101 | |
| | 102 | /* |
| | 103 | * Reset |
| | 104 | */ |
| | 105 | void CTcDataStream::reset() |
| | 106 | { |
| | 107 | /* move the write pointer back to the start */ |
| | 108 | ofs_ = 0; |
| | 109 | obj_file_start_ofs_ = 0; |
| | 110 | |
| | 111 | /* back to the first page */ |
| | 112 | page_cur_ = 0; |
| | 113 | |
| | 114 | /* set up to write to the first page, if we have any pages at all */ |
| | 115 | if (pages_ != 0) |
| | 116 | { |
| | 117 | /* we have all of the first page available again */ |
| | 118 | wp_ = calc_addr(0); |
| | 119 | rem_ = TCCS_PAGE_SIZE; |
| | 120 | } |
| | 121 | |
| | 122 | /* reset the allocator */ |
| | 123 | allocator_->reset(); |
| | 124 | |
| | 125 | /* |
| | 126 | * forget all of the anchors (no need to delete them explicitly - |
| | 127 | * they were allocated from our allocator pool, which we've reset to |
| | 128 | * completely discard everything it contained) |
| | 129 | */ |
| | 130 | first_anchor_ = last_anchor_ = 0; |
| | 131 | } |
| | 132 | |
| | 133 | /* |
| | 134 | * Decrement the write offset |
| | 135 | */ |
| | 136 | void CTcDataStream::dec_ofs(int amount) |
| | 137 | { |
| | 138 | /* adjust the offset */ |
| | 139 | ofs_ -= amount; |
| | 140 | |
| | 141 | /* |
| | 142 | * calculate the new page we're on, since this may take us to a |
| | 143 | * different page |
| | 144 | */ |
| | 145 | page_cur_ = ofs_ / TCCS_PAGE_SIZE; |
| | 146 | |
| | 147 | /* calculate the remaining size in this page */ |
| | 148 | rem_ = TCCS_PAGE_SIZE - (ofs_ % TCCS_PAGE_SIZE); |
| | 149 | |
| | 150 | /* calculate the current write pointer */ |
| | 151 | wp_ = calc_addr(ofs_); |
| | 152 | } |
| | 153 | |
| | 154 | /* |
| | 155 | * Get a pointer to a block at a given offset and a given length. |
| | 156 | */ |
| | 157 | const char *CTcDataStream::get_block_ptr(ulong ofs, |
| | 158 | ulong requested_len, |
| | 159 | ulong *available_len) |
| | 160 | { |
| | 161 | size_t page_rem; |
| | 162 | |
| | 163 | /* |
| | 164 | * determine how much is left on the page containing the offset |
| | 165 | * after the given offset |
| | 166 | */ |
| | 167 | page_rem = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE); |
| | 168 | |
| | 169 | /* |
| | 170 | * if the amount remaining on the page is greater than the request |
| | 171 | * length, the available length is the entire request; otherwise, |
| | 172 | * the available length is the amount remaining on the page |
| | 173 | */ |
| | 174 | if (page_rem >= requested_len) |
| | 175 | *available_len = requested_len; |
| | 176 | else |
| | 177 | *available_len = page_rem; |
| | 178 | |
| | 179 | /* return the address at this offset */ |
| | 180 | return calc_addr(ofs); |
| | 181 | } |
| | 182 | |
| | 183 | |
| | 184 | /* |
| | 185 | * Write bytes to the stream at an earlier offset |
| | 186 | */ |
| | 187 | void CTcDataStream::write_at(ulong ofs, const char *buf, size_t len) |
| | 188 | { |
| | 189 | /* if we're writing to the current offset, use the normal writer */ |
| | 190 | if (ofs == ofs_) |
| | 191 | write(buf, len); |
| | 192 | |
| | 193 | /* |
| | 194 | * log an internal error, and skip writing anything, if the desired |
| | 195 | * range of offsets has not been previously written |
| | 196 | */ |
| | 197 | if (ofs + len > ofs_) |
| | 198 | G_tok->throw_internal_error(TCERR_WRITEAT_PAST_END); |
| | 199 | |
| | 200 | /* write the data to each page it spans */ |
| | 201 | while (len != 0) |
| | 202 | { |
| | 203 | size_t cur; |
| | 204 | |
| | 205 | /* |
| | 206 | * determine how much is left on the page containing the current |
| | 207 | * starting offset |
| | 208 | */ |
| | 209 | cur = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE); |
| | 210 | |
| | 211 | /* |
| | 212 | * figure out how much we can copy - copy the whole remaining |
| | 213 | * size, but no more than the amount remaining on this page |
| | 214 | */ |
| | 215 | if (cur > len) |
| | 216 | cur = len; |
| | 217 | |
| | 218 | /* copy the data */ |
| | 219 | memcpy(calc_addr(ofs), buf, cur); |
| | 220 | |
| | 221 | /* advance past this chunk */ |
| | 222 | len -= cur; |
| | 223 | ofs += cur; |
| | 224 | buf += cur; |
| | 225 | } |
| | 226 | } |
| | 227 | |
| | 228 | /* |
| | 229 | * Copy a chunk of the stream to the given buffer |
| | 230 | */ |
| | 231 | void CTcDataStream::copy_to_buf(char *buf, ulong start_ofs, ulong len) |
| | 232 | { |
| | 233 | /* read the data from each page that the block spans */ |
| | 234 | while (len != 0) |
| | 235 | { |
| | 236 | size_t cur; |
| | 237 | |
| | 238 | /* |
| | 239 | * determine how much is left on the page containing the current |
| | 240 | * starting offset |
| | 241 | */ |
| | 242 | cur = TCCS_PAGE_SIZE - (start_ofs % TCCS_PAGE_SIZE); |
| | 243 | |
| | 244 | /* |
| | 245 | * figure out how much we can copy - copy the whole remaining |
| | 246 | * size, but no more than the amount remaining on this page |
| | 247 | */ |
| | 248 | if (cur > len) |
| | 249 | cur = (size_t)len; |
| | 250 | |
| | 251 | /* copy the data */ |
| | 252 | memcpy(buf, calc_addr(start_ofs), cur); |
| | 253 | |
| | 254 | /* advance past this chunk */ |
| | 255 | len -= cur; |
| | 256 | start_ofs += cur; |
| | 257 | buf += cur; |
| | 258 | } |
| | 259 | } |
| | 260 | |
| | 261 | /* |
| | 262 | * Reserve space |
| | 263 | */ |
| | 264 | ulong CTcDataStream::reserve(size_t len) |
| | 265 | { |
| | 266 | ulong ret; |
| | 267 | |
| | 268 | /* we'll always return the offset current before the call */ |
| | 269 | ret = ofs_; |
| | 270 | |
| | 271 | /* if we have space on the current page, it's easy */ |
| | 272 | if (len <= rem_) |
| | 273 | { |
| | 274 | /* advance the output pointers */ |
| | 275 | ofs_ += len; |
| | 276 | wp_ += len; |
| | 277 | rem_ -= len; |
| | 278 | } |
| | 279 | else |
| | 280 | { |
| | 281 | /* keep going until we satisfy the request */ |
| | 282 | do |
| | 283 | { |
| | 284 | size_t cur; |
| | 285 | |
| | 286 | /* if necessary, allocate more memory */ |
| | 287 | if (rem_ == 0) |
| | 288 | alloc_page(); |
| | 289 | |
| | 290 | /* limit this chunk to the space remaining on the current page */ |
| | 291 | cur = len; |
| | 292 | if (cur > rem_) |
| | 293 | cur = rem_; |
| | 294 | |
| | 295 | /* skip past this chunk */ |
| | 296 | ofs_ += cur; |
| | 297 | wp_ += cur; |
| | 298 | rem_ -= cur; |
| | 299 | len -= cur; |
| | 300 | |
| | 301 | } while (len != 0); |
| | 302 | } |
| | 303 | |
| | 304 | /* return the starting offset */ |
| | 305 | return ret; |
| | 306 | } |
| | 307 | |
| | 308 | /* |
| | 309 | * Append data from another stream. The source stream is permanently |
| | 310 | * moved to the new stream, destroying the original stream. |
| | 311 | */ |
| | 312 | void CTcDataStream::append_stream(CTcDataStream *stream) |
| | 313 | { |
| | 314 | ulong rem; |
| | 315 | ulong ofs; |
| | 316 | ulong start_ofs; |
| | 317 | CTcStreamAnchor *anchor; |
| | 318 | CTcStreamAnchor *nxt; |
| | 319 | |
| | 320 | /* remember the starting offset of the copy in my stream */ |
| | 321 | start_ofs = get_ofs(); |
| | 322 | |
| | 323 | /* copy all data from the other stream */ |
| | 324 | for (ofs = 0, rem = stream->get_ofs() ; rem != 0 ; ) |
| | 325 | { |
| | 326 | ulong request; |
| | 327 | const char *ptr; |
| | 328 | ulong actual; |
| | 329 | |
| | 330 | /* |
| | 331 | * request as much as possible from the other stream, up to the |
| | 332 | * remaining length or 64k, whichever is smaller |
| | 333 | */ |
| | 334 | request = 65535; |
| | 335 | if (rem < request) |
| | 336 | request = rem; |
| | 337 | |
| | 338 | /* get the chunk from the source stream */ |
| | 339 | ptr = stream->get_block_ptr(ofs, request, &actual); |
| | 340 | |
| | 341 | /* |
| | 342 | * write this chunk (which we know is less than 64k and can thus |
| | 343 | * be safely cast to size_t, even on 16-bit machines) |
| | 344 | */ |
| | 345 | write(ptr, (size_t)actual); |
| | 346 | |
| | 347 | /* advance our counters */ |
| | 348 | rem -= actual; |
| | 349 | ofs += actual; |
| | 350 | } |
| | 351 | |
| | 352 | /* |
| | 353 | * Now copy all of the anchors from the source stream to our stream. |
| | 354 | * This will ensure that fixups in the other stream have |
| | 355 | * corresponding fixups in this stream. Note that we must adjust |
| | 356 | * the offset of each copied anchor by the offset of the start of |
| | 357 | * the copied data in our stream. |
| | 358 | */ |
| | 359 | for (anchor = stream->get_first_anchor() ; anchor != 0 ; anchor = nxt) |
| | 360 | { |
| | 361 | /* |
| | 362 | * remember the old link to the next anchor, since we're going |
| | 363 | * to move the anchor to my list and thus forget about its |
| | 364 | * position in the old list |
| | 365 | */ |
| | 366 | nxt = anchor->nxt_; |
| | 367 | |
| | 368 | /* adjust the anchor's offset */ |
| | 369 | anchor->ofs_ += start_ofs; |
| | 370 | |
| | 371 | /* unlink the anchor from its old stream */ |
| | 372 | anchor->nxt_ = 0; |
| | 373 | |
| | 374 | /* link it in to my anchor list */ |
| | 375 | if (last_anchor_ != 0) |
| | 376 | last_anchor_->nxt_ = anchor; |
| | 377 | else |
| | 378 | first_anchor_ = anchor; |
| | 379 | last_anchor_ = anchor; |
| | 380 | } |
| | 381 | } |
| | 382 | |
| | 383 | /* |
| | 384 | * Write bytes to the stream |
| | 385 | */ |
| | 386 | void CTcDataStream::write(const char *buf, size_t len) |
| | 387 | { |
| | 388 | /* |
| | 389 | * if possible, write it in one go (this is for efficiency, so that |
| | 390 | * we can avoid making a few comparisons in the most common case) |
| | 391 | */ |
| | 392 | if (len <= rem_) |
| | 393 | { |
| | 394 | /* write the data */ |
| | 395 | memcpy(wp_, buf, len); |
| | 396 | |
| | 397 | /* advance the output pointers */ |
| | 398 | ofs_ += len; |
| | 399 | wp_ += len; |
| | 400 | rem_ -= len; |
| | 401 | } |
| | 402 | else |
| | 403 | { |
| | 404 | /* keep going until we satisfy the request */ |
| | 405 | do |
| | 406 | { |
| | 407 | size_t cur; |
| | 408 | |
| | 409 | /* if necessary, allocate more memory */ |
| | 410 | if (rem_ == 0) |
| | 411 | alloc_page(); |
| | 412 | |
| | 413 | /* limit this chunk to the space remaining on the current page */ |
| | 414 | cur = len; |
| | 415 | if (cur > rem_) |
| | 416 | cur = rem_; |
| | 417 | |
| | 418 | /* copy it to the page */ |
| | 419 | memcpy(wp_, buf, cur); |
| | 420 | |
| | 421 | /* skip past the space written in the destination */ |
| | 422 | ofs_ += cur; |
| | 423 | wp_ += cur; |
| | 424 | rem_ -= cur; |
| | 425 | |
| | 426 | /* advance past the space in the source */ |
| | 427 | buf += cur; |
| | 428 | len -= cur; |
| | 429 | |
| | 430 | } while (len != 0); |
| | 431 | } |
| | 432 | } |
| | 433 | |
| | 434 | /* |
| | 435 | * allocate a new page |
| | 436 | */ |
| | 437 | void CTcDataStream::alloc_page() |
| | 438 | { |
| | 439 | /* |
| | 440 | * if we're coming back to a page that was previously allocated, we |
| | 441 | * need merely re-establish the existing page |
| | 442 | */ |
| | 443 | if (page_cur_ + 1 < page_cnt_) |
| | 444 | { |
| | 445 | /* move to the next page */ |
| | 446 | ++page_cur_; |
| | 447 | |
| | 448 | /* start writing at the start of the page */ |
| | 449 | wp_ = pages_[page_cur_]; |
| | 450 | rem_ = TCCS_PAGE_SIZE; |
| | 451 | |
| | 452 | /* we're done */ |
| | 453 | return; |
| | 454 | } |
| | 455 | |
| | 456 | /* |
| | 457 | * if we don't have room for a new page in the page array, expand |
| | 458 | * the page array |
| | 459 | */ |
| | 460 | if (page_cnt_ >= page_slots_) |
| | 461 | { |
| | 462 | /* increase the page slot count */ |
| | 463 | page_slots_ += 100; |
| | 464 | |
| | 465 | /* allocate or reallocate the page array */ |
| | 466 | if (pages_ == 0) |
| | 467 | pages_ = (char **)t3malloc(page_slots_ * sizeof(pages_[0])); |
| | 468 | else |
| | 469 | pages_ = (char **)t3realloc(pages_, |
| | 470 | page_slots_ * sizeof(pages_[0])); |
| | 471 | |
| | 472 | /* if that failed, throw an error */ |
| | 473 | if (pages_ == 0) |
| | 474 | err_throw(TCERR_CODEGEN_NO_MEM); |
| | 475 | } |
| | 476 | |
| | 477 | /* allocate the new page */ |
| | 478 | pages_[page_cnt_] = (char *)t3malloc(TCCS_PAGE_SIZE); |
| | 479 | |
| | 480 | /* throw an error if we couldn't allocate the page */ |
| | 481 | if (pages_[page_cnt_] == 0) |
| | 482 | err_throw(TCERR_CODEGEN_NO_MEM); |
| | 483 | |
| | 484 | /* start writing at the start of the new page */ |
| | 485 | wp_ = pages_[page_cnt_]; |
| | 486 | |
| | 487 | /* the entire page is free */ |
| | 488 | rem_ = TCCS_PAGE_SIZE; |
| | 489 | |
| | 490 | /* make the new page the current page */ |
| | 491 | page_cur_ = page_cnt_; |
| | 492 | |
| | 493 | /* count the new page */ |
| | 494 | ++page_cnt_; |
| | 495 | } |
| | 496 | |
| | 497 | /* |
| | 498 | * Add an absolute fixup for this stream at the current write offset. |
| | 499 | */ |
| | 500 | void CTcDataStream::add_abs_fixup(CTcAbsFixup **list_head) |
| | 501 | { |
| | 502 | /* add the fixup to the list at my current write location */ |
| | 503 | CTcAbsFixup::add_abs_fixup(list_head, this, get_ofs()); |
| | 504 | } |
| | 505 | |
| | 506 | |
| | 507 | /* |
| | 508 | * Add an anchor at the current offset. |
| | 509 | */ |
| | 510 | CTcStreamAnchor *CTcDataStream::add_anchor(CTcSymbol *owner_sym, |
| | 511 | CTcAbsFixup **fixup_list_head, |
| | 512 | ulong ofs) |
| | 513 | { |
| | 514 | CTcStreamAnchor *anchor; |
| | 515 | |
| | 516 | /* allocate the anchor, giving it our current offset */ |
| | 517 | anchor = new (allocator_) CTcStreamAnchor(owner_sym, |
| | 518 | fixup_list_head, ofs); |
| | 519 | |
| | 520 | /* append it to our list */ |
| | 521 | if (last_anchor_ != 0) |
| | 522 | last_anchor_->nxt_ = anchor; |
| | 523 | else |
| | 524 | first_anchor_ = anchor; |
| | 525 | last_anchor_ = anchor; |
| | 526 | |
| | 527 | /* return the new anchor */ |
| | 528 | return anchor; |
| | 529 | } |
| | 530 | |
| | 531 | /* |
| | 532 | * Find an anchor with the given stream offset |
| | 533 | */ |
| | 534 | CTcStreamAnchor *CTcDataStream::find_anchor(ulong ofs) const |
| | 535 | { |
| | 536 | CTcStreamAnchor *cur; |
| | 537 | |
| | 538 | /* scan the anchor list */ |
| | 539 | for (cur = first_anchor_ ; cur != 0 ; cur = cur->nxt_) |
| | 540 | { |
| | 541 | /* if this one has the desired offset, return it */ |
| | 542 | if (cur->get_ofs() == ofs) |
| | 543 | return cur; |
| | 544 | } |
| | 545 | |
| | 546 | /* didn't find it */ |
| | 547 | return 0; |
| | 548 | } |
| | 549 | |
| | 550 | /* |
| | 551 | * Write an object ID |
| | 552 | */ |
| | 553 | void CTcDataStream::write_obj_id(ulong obj_id) |
| | 554 | { |
| | 555 | /* |
| | 556 | * if there's an object ID fixup list, and this is a valid object |
| | 557 | * reference (not a 'nil' reference), add this reference |
| | 558 | */ |
| | 559 | if (G_keep_objfixups && obj_id != TCTARG_INVALID_OBJ) |
| | 560 | CTcIdFixup::add_fixup(&G_objfixup, this, get_ofs(), obj_id); |
| | 561 | |
| | 562 | /* write the ID */ |
| | 563 | write4(obj_id); |
| | 564 | } |
| | 565 | |
| | 566 | /* |
| | 567 | * Write an object ID self-reference |
| | 568 | */ |
| | 569 | void CTcDataStream::write_obj_id_selfref(CTcSymObj *obj_sym) |
| | 570 | { |
| | 571 | /* |
| | 572 | * Add a fixup list entry to the symbol. This type of reference |
| | 573 | * must be kept with the symbol rather than in the global list, |
| | 574 | * because we must apply this type of fixup each time we renumber |
| | 575 | * the symbol. |
| | 576 | */ |
| | 577 | obj_sym->add_self_ref_fixup(this, get_ofs()); |
| | 578 | |
| | 579 | /* write the ID to the stream */ |
| | 580 | write4(obj_sym->get_obj_id()); |
| | 581 | } |
| | 582 | |
| | 583 | |
| | 584 | /* |
| | 585 | * Write a property ID |
| | 586 | */ |
| | 587 | void CTcDataStream::write_prop_id(uint prop_id) |
| | 588 | { |
| | 589 | /* if there's an object ID fixup list, add this reference */ |
| | 590 | if (G_keep_propfixups) |
| | 591 | CTcIdFixup::add_fixup(&G_propfixup, this, get_ofs(), prop_id); |
| | 592 | |
| | 593 | /* write the ID */ |
| | 594 | write2(prop_id); |
| | 595 | } |
| | 596 | |
| | 597 | /* |
| | 598 | * Write an enumerator ID |
| | 599 | */ |
| | 600 | void CTcDataStream::write_enum_id(ulong enum_id) |
| | 601 | { |
| | 602 | /* if there's a fixup list, add this reference */ |
| | 603 | if (G_keep_enumfixups) |
| | 604 | CTcIdFixup::add_fixup(&G_enumfixup, this, get_ofs(), enum_id); |
| | 605 | |
| | 606 | /* write the ID */ |
| | 607 | write4(enum_id); |
| | 608 | } |
| | 609 | |
| | 610 | /* |
| | 611 | * Write the stream, its anchors, and its fixups to an object file. |
| | 612 | */ |
| | 613 | void CTcDataStream::write_to_object_file(CVmFile *fp) |
| | 614 | { |
| | 615 | ulong ofs; |
| | 616 | CTcStreamAnchor *anchor; |
| | 617 | long cnt; |
| | 618 | |
| | 619 | /* |
| | 620 | * First, write the data stream bytes. Write the length prefix |
| | 621 | * followed by the data. Just blast the whole thing out in one huge |
| | 622 | * byte stream, one page at a time. |
| | 623 | */ |
| | 624 | fp->write_int4(ofs_); |
| | 625 | |
| | 626 | /* write the data one page at a time */ |
| | 627 | for (ofs = 0 ; ofs < ofs_ ; ) |
| | 628 | { |
| | 629 | size_t cur; |
| | 630 | |
| | 631 | /* |
| | 632 | * write out one whole page, or the balance of the current page |
| | 633 | * if we have less than a whole page remaining |
| | 634 | */ |
| | 635 | cur = TCCS_PAGE_SIZE; |
| | 636 | if (ofs + cur > ofs_) |
| | 637 | cur = (size_t)(ofs_ - ofs); |
| | 638 | |
| | 639 | /* write out this chunk */ |
| | 640 | fp->write_bytes(calc_addr(ofs), cur); |
| | 641 | |
| | 642 | /* move to the next page's offset */ |
| | 643 | ofs += cur; |
| | 644 | } |
| | 645 | |
| | 646 | /* count the anchors */ |
| | 647 | for (cnt = 0, anchor = first_anchor_ ; anchor != 0 ; |
| | 648 | anchor = anchor->nxt_, ++cnt) ; |
| | 649 | |
| | 650 | /* write the count */ |
| | 651 | fp->write_int4(cnt); |
| | 652 | |
| | 653 | /* |
| | 654 | * Write all of the anchors, and all of their fixups. (We have the |
| | 655 | * code to write the anchor and fixup information in-line here for |
| | 656 | * efficiency - there will normally be a large number of these tiny |
| | 657 | * objects, so anything we can do to improve the speed of this loop |
| | 658 | * will help quite a lot in the overall write performance.) |
| | 659 | */ |
| | 660 | for (anchor = first_anchor_ ; anchor != 0 ; anchor = anchor->nxt_) |
| | 661 | { |
| | 662 | char buf[6]; |
| | 663 | |
| | 664 | /* write the stream offset */ |
| | 665 | oswp4(buf, anchor->get_ofs()); |
| | 666 | |
| | 667 | /* |
| | 668 | * If the anchor has an external fixup list, write the symbol's |
| | 669 | * name to the file; otherwise write a zero length to indicate |
| | 670 | * that we have an internal fixup list. |
| | 671 | */ |
| | 672 | if (anchor->get_fixup_owner_sym() == 0) |
| | 673 | { |
| | 674 | /* no external list - indicate with a zero symbol length */ |
| | 675 | oswp2(buf+4, 0); |
| | 676 | fp->write_bytes(buf, 6); |
| | 677 | } |
| | 678 | else |
| | 679 | { |
| | 680 | /* external list - write the symbol length and name */ |
| | 681 | oswp2(buf+4, anchor->get_fixup_owner_sym()->get_sym_len()); |
| | 682 | fp->write_bytes(buf, 6); |
| | 683 | fp->write_bytes(anchor->get_fixup_owner_sym()->get_sym(), |
| | 684 | anchor->get_fixup_owner_sym()->get_sym_len()); |
| | 685 | } |
| | 686 | |
| | 687 | /* write the fixup list */ |
| | 688 | CTcAbsFixup:: |
| | 689 | write_fixup_list_to_object_file(fp, *anchor->fixup_list_head_); |
| | 690 | } |
| | 691 | } |
| | 692 | |
| | 693 | /* |
| | 694 | * Read a stream from an object file |
| | 695 | */ |
| | 696 | void CTcDataStream::load_object_file(CVmFile *fp, |
| | 697 | const textchar_t *fname) |
| | 698 | { |
| | 699 | ulong stream_len; |
| | 700 | ulong rem; |
| | 701 | char buf[1024]; |
| | 702 | ulong start_ofs; |
| | 703 | ulong anchor_cnt; |
| | 704 | |
| | 705 | /* read the length of the stream */ |
| | 706 | stream_len = fp->read_uint4(); |
| | 707 | |
| | 708 | /* remember my starting offset */ |
| | 709 | start_ofs = get_ofs(); |
| | 710 | |
| | 711 | /* read the stream bytes */ |
| | 712 | for (rem = stream_len ; rem != 0 ; ) |
| | 713 | { |
| | 714 | size_t cur; |
| | 715 | |
| | 716 | /* read up to a buffer-full, or however much is left */ |
| | 717 | cur = sizeof(buf); |
| | 718 | if (cur > rem) |
| | 719 | cur = rem; |
| | 720 | |
| | 721 | /* read this chunk */ |
| | 722 | fp->read_bytes(buf, cur); |
| | 723 | |
| | 724 | /* add this chunk to the stream */ |
| | 725 | write(buf, cur); |
| | 726 | |
| | 727 | /* deduct the amount we've read from the amount remaining */ |
| | 728 | rem -= cur; |
| | 729 | } |
| | 730 | |
| | 731 | /* |
| | 732 | * Read the anchors. For each anchor, we must fix up the anchor by |
| | 733 | * adding the base address of the stream we just read - the original |
| | 734 | * anchor offsets in the object file reflect a base stream offset of |
| | 735 | * zero, but we could be loading the stream after a bunch of other |
| | 736 | * data have already been loaded into the stream. |
| | 737 | * |
| | 738 | * First, read the number of anchors, then loop through the anchors |
| | 739 | * and read each one. |
| | 740 | */ |
| | 741 | for (anchor_cnt = fp->read_uint4() ; anchor_cnt != 0 ; |
| | 742 | --anchor_cnt) |
| | 743 | { |
| | 744 | ulong anchor_ofs; |
| | 745 | size_t sym_len; |
| | 746 | CTcStreamAnchor *anchor; |
| | 747 | |
| | 748 | /* read this anchor */ |
| | 749 | fp->read_bytes(buf, 6); |
| | 750 | |
| | 751 | /* get the offset, and adjust for the new stream base offset */ |
| | 752 | anchor_ofs = t3rp4u(buf) + start_ofs; |
| | 753 | |
| | 754 | /* get the length of the owning symbol's name, if any */ |
| | 755 | sym_len = osrp2(buf+4); |
| | 756 | |
| | 757 | /* if there's a symbol name, read it */ |
| | 758 | if (sym_len != 0) |
| | 759 | { |
| | 760 | CTcSymbol *owner_sym; |
| | 761 | |
| | 762 | /* read the symbol name */ |
| | 763 | fp->read_bytes(buf, sym_len); |
| | 764 | |
| | 765 | /* look it up in the global symbol table */ |
| | 766 | owner_sym = G_prs->get_global_symtab()->find(buf, sym_len); |
| | 767 | if (owner_sym == 0) |
| | 768 | { |
| | 769 | /* |
| | 770 | * the owner symbol doesn't exist - this is an internal |
| | 771 | * inconsistency in the object file, because the anchor |
| | 772 | * symbol must always be defined in the same file and |
| | 773 | * hence should have been loaded already; complain and |
| | 774 | * go on |
| | 775 | */ |
| | 776 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 777 | TCERR_OBJFILE_INT_SYM_MISSING, |
| | 778 | (int)sym_len, buf, fname); |
| | 779 | |
| | 780 | /* we can't create the anchor */ |
| | 781 | anchor = 0; |
| | 782 | } |
| | 783 | else |
| | 784 | { |
| | 785 | /* create the anchor based on the symbol */ |
| | 786 | anchor = add_anchor(owner_sym, |
| | 787 | owner_sym->get_fixup_list_anchor(), |
| | 788 | anchor_ofs); |
| | 789 | |
| | 790 | /* set the anchor in the symbol */ |
| | 791 | owner_sym->set_anchor(anchor); |
| | 792 | } |
| | 793 | } |
| | 794 | else |
| | 795 | { |
| | 796 | /* create the anchor with no external references */ |
| | 797 | anchor = add_anchor(0, 0, anchor_ofs); |
| | 798 | } |
| | 799 | |
| | 800 | /* load the fixup list */ |
| | 801 | CTcAbsFixup:: |
| | 802 | load_fixup_list_from_object_file(fp, fname, |
| | 803 | anchor->fixup_list_head_); |
| | 804 | } |
| | 805 | } |
| | 806 | |
| | 807 | /* |
| | 808 | * Given a stream ID, get the stream |
| | 809 | */ |
| | 810 | CTcDataStream *CTcDataStream:: |
| | 811 | get_stream_from_id(char stream_id, const textchar_t *obj_fname) |
| | 812 | { |
| | 813 | switch(stream_id) |
| | 814 | { |
| | 815 | case TCGEN_DATA_STREAM: |
| | 816 | return G_ds; |
| | 817 | |
| | 818 | case TCGEN_CODE_STREAM: |
| | 819 | return G_cs_main; |
| | 820 | |
| | 821 | case TCGEN_STATIC_CODE_STREAM: |
| | 822 | return G_cs_static; |
| | 823 | |
| | 824 | case TCGEN_OBJECT_STREAM: |
| | 825 | return G_os; |
| | 826 | |
| | 827 | case TCGEN_ICMOD_STREAM: |
| | 828 | return G_icmod_stream; |
| | 829 | |
| | 830 | case TCGEN_BIGNUM_STREAM: |
| | 831 | return G_bignum_stream; |
| | 832 | |
| | 833 | case TCGEN_STATIC_INIT_ID_STREAM: |
| | 834 | return G_static_init_id_stream; |
| | 835 | |
| | 836 | default: |
| | 837 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 838 | TCERR_OBJFILE_INVAL_STREAM_ID, obj_fname); |
| | 839 | return 0; |
| | 840 | } |
| | 841 | } |
| | 842 | |
| | 843 | |
| | 844 | /* ------------------------------------------------------------------------ */ |
| | 845 | /* |
| | 846 | * Code Stream |
| | 847 | */ |
| | 848 | |
| | 849 | /* |
| | 850 | * create the code stream |
| | 851 | */ |
| | 852 | CTcCodeStream::CTcCodeStream(char stream_id) |
| | 853 | : CTcDataStream(stream_id) |
| | 854 | { |
| | 855 | /* no switch yet */ |
| | 856 | cur_switch_ = 0; |
| | 857 | |
| | 858 | /* no enclosing statement yet */ |
| | 859 | enclosing_ = 0; |
| | 860 | |
| | 861 | /* no code body being generated yet */ |
| | 862 | code_body_ = 0; |
| | 863 | |
| | 864 | /* start writing at offset zero */ |
| | 865 | ofs_ = 0; |
| | 866 | |
| | 867 | /* no symbol tables yet */ |
| | 868 | symtab_ = 0; |
| | 869 | goto_symtab_ = 0; |
| | 870 | |
| | 871 | /* no labels yet */ |
| | 872 | active_lbl_ = 0; |
| | 873 | free_lbl_ = 0; |
| | 874 | |
| | 875 | /* no fixups yet */ |
| | 876 | free_fixup_ = 0; |
| | 877 | |
| | 878 | /* allocate an initial set of line record pages */ |
| | 879 | line_pages_alloc_ = 0; |
| | 880 | line_pages_ = 0; |
| | 881 | alloc_line_pages(5); |
| | 882 | |
| | 883 | /* no line records in use yet */ |
| | 884 | line_cnt_ = 0; |
| | 885 | } |
| | 886 | |
| | 887 | |
| | 888 | /* |
| | 889 | * destroy the code stream |
| | 890 | */ |
| | 891 | CTcCodeStream::~CTcCodeStream() |
| | 892 | { |
| | 893 | size_t i; |
| | 894 | |
| | 895 | /* release all active labels */ |
| | 896 | release_labels(); |
| | 897 | |
| | 898 | /* delete the line records pages */ |
| | 899 | for (i = 0 ; i < line_pages_alloc_ ; ++i) |
| | 900 | t3free(line_pages_[i]); |
| | 901 | |
| | 902 | /* delete the master list of pages */ |
| | 903 | t3free(line_pages_); |
| | 904 | } |
| | 905 | |
| | 906 | /* |
| | 907 | * Reset |
| | 908 | */ |
| | 909 | void CTcCodeStream::reset() |
| | 910 | { |
| | 911 | /* inherit default */ |
| | 912 | CTcDataStream::reset(); |
| | 913 | |
| | 914 | /* clear the line records */ |
| | 915 | clear_line_recs(); |
| | 916 | |
| | 917 | /* |
| | 918 | * forget all of the labels and fixups - they're allocated from the |
| | 919 | * allocator pool, which we've completely reset now |
| | 920 | */ |
| | 921 | free_lbl_ = active_lbl_ = 0; |
| | 922 | free_fixup_ = 0; |
| | 923 | |
| | 924 | /* forget all of the symbol tables */ |
| | 925 | symtab_ = 0; |
| | 926 | goto_symtab_ = 0; |
| | 927 | |
| | 928 | /* forget the frame list */ |
| | 929 | frame_head_ = frame_tail_ = 0; |
| | 930 | cur_frame_ = 0; |
| | 931 | frame_cnt_ = 0; |
| | 932 | |
| | 933 | /* forget all of the statement settings */ |
| | 934 | cur_switch_ = 0; |
| | 935 | enclosing_ = 0; |
| | 936 | code_body_ = 0; |
| | 937 | |
| | 938 | /* presume 'self' is not available */ |
| | 939 | self_available_ = FALSE; |
| | 940 | } |
| | 941 | |
| | 942 | /* |
| | 943 | * Allocate line record pages |
| | 944 | */ |
| | 945 | void CTcCodeStream::alloc_line_pages(size_t number_to_add) |
| | 946 | { |
| | 947 | size_t siz; |
| | 948 | size_t i; |
| | 949 | |
| | 950 | /* create or expand the master page array */ |
| | 951 | siz = (line_pages_alloc_ + number_to_add) * sizeof(tcgen_line_page_t *); |
| | 952 | if (line_pages_ == 0) |
| | 953 | line_pages_ = (tcgen_line_page_t **)t3malloc(siz); |
| | 954 | else |
| | 955 | line_pages_ = (tcgen_line_page_t **)t3realloc(line_pages_, siz); |
| | 956 | |
| | 957 | /* allocate the new pages */ |
| | 958 | for (i = line_pages_alloc_ ; i < line_pages_alloc_ + number_to_add ; ++i) |
| | 959 | { |
| | 960 | /* allocate this page */ |
| | 961 | line_pages_[i] = (tcgen_line_page_t *) |
| | 962 | t3malloc(sizeof(tcgen_line_page_t)); |
| | 963 | } |
| | 964 | |
| | 965 | /* remember the new allocation */ |
| | 966 | line_pages_alloc_ += number_to_add; |
| | 967 | } |
| | 968 | |
| | 969 | /* |
| | 970 | * Allocate a new label object |
| | 971 | */ |
| | 972 | CTcCodeLabel *CTcCodeStream::alloc_label() |
| | 973 | { |
| | 974 | CTcCodeLabel *ret; |
| | 975 | |
| | 976 | /* if there's anything in the free list, use it */ |
| | 977 | if (free_lbl_ != 0) |
| | 978 | { |
| | 979 | /* take the first one off the free list */ |
| | 980 | ret = free_lbl_; |
| | 981 | |
| | 982 | /* unlink it from the list */ |
| | 983 | free_lbl_ = free_lbl_->nxt; |
| | 984 | } |
| | 985 | else |
| | 986 | { |
| | 987 | /* allocate a new label */ |
| | 988 | ret = new (allocator_) CTcCodeLabel; |
| | 989 | |
| | 990 | /* throw an error if allocation failed */ |
| | 991 | if (ret == 0) |
| | 992 | err_throw(TCERR_CODEGEN_NO_MEM); |
| | 993 | } |
| | 994 | |
| | 995 | /* add the label to the active list */ |
| | 996 | ret->nxt = active_lbl_; |
| | 997 | active_lbl_ = ret; |
| | 998 | |
| | 999 | /* return the allocated label */ |
| | 1000 | return ret; |
| | 1001 | } |
| | 1002 | |
| | 1003 | /* |
| | 1004 | * Allocate a new fixup object |
| | 1005 | */ |
| | 1006 | CTcLabelFixup *CTcCodeStream::alloc_fixup() |
| | 1007 | { |
| | 1008 | CTcLabelFixup *ret; |
| | 1009 | |
| | 1010 | /* if there's anything in the free list, use it */ |
| | 1011 | if (free_fixup_ != 0) |
| | 1012 | { |
| | 1013 | /* take the first one off the free list */ |
| | 1014 | ret = free_fixup_; |
| | 1015 | |
| | 1016 | /* unlink it from the list */ |
| | 1017 | free_fixup_ = free_fixup_->nxt; |
| | 1018 | } |
| | 1019 | else |
| | 1020 | { |
| | 1021 | /* allocate a new fixup */ |
| | 1022 | ret = new (allocator_) CTcLabelFixup; |
| | 1023 | |
| | 1024 | /* throw an error if allocation failed */ |
| | 1025 | if (ret == 0) |
| | 1026 | err_throw(TCERR_CODEGEN_NO_MEM); |
| | 1027 | } |
| | 1028 | |
| | 1029 | /* return the allocated fixup */ |
| | 1030 | return ret; |
| | 1031 | } |
| | 1032 | |
| | 1033 | /* |
| | 1034 | * Release all active labels. If any labels are undefined, log an |
| | 1035 | * internal error. |
| | 1036 | */ |
| | 1037 | void CTcCodeStream::release_labels() |
| | 1038 | { |
| | 1039 | int err_cnt; |
| | 1040 | |
| | 1041 | /* we haven't found any errors yet */ |
| | 1042 | err_cnt = 0; |
| | 1043 | |
| | 1044 | /* run through the list of active labels */ |
| | 1045 | while (active_lbl_ != 0) |
| | 1046 | { |
| | 1047 | CTcCodeLabel *lbl; |
| | 1048 | |
| | 1049 | /* pull this label off of the active list */ |
| | 1050 | lbl = active_lbl_; |
| | 1051 | active_lbl_ = active_lbl_->nxt; |
| | 1052 | |
| | 1053 | /* put this label on the free list */ |
| | 1054 | lbl->nxt = free_lbl_; |
| | 1055 | free_lbl_ = lbl; |
| | 1056 | |
| | 1057 | /* check for unresolved fixups */ |
| | 1058 | while (lbl->fhead != 0) |
| | 1059 | { |
| | 1060 | CTcLabelFixup *fixup; |
| | 1061 | |
| | 1062 | /* pull this fixup off of the active list */ |
| | 1063 | fixup = lbl->fhead; |
| | 1064 | lbl->fhead = lbl->fhead->nxt; |
| | 1065 | |
| | 1066 | /* put this fixup on the free list */ |
| | 1067 | fixup->nxt = free_fixup_; |
| | 1068 | free_fixup_ = fixup; |
| | 1069 | |
| | 1070 | /* count the unresolved label */ |
| | 1071 | ++err_cnt; |
| | 1072 | } |
| | 1073 | } |
| | 1074 | |
| | 1075 | /* |
| | 1076 | * if we found any unresolved fixups, log the error; there's not |
| | 1077 | * much point in logging each error individually, since this is an |
| | 1078 | * internal compiler error that the user can't do anything about, |
| | 1079 | * but at least give the user a count for compiler diagnostic |
| | 1080 | * purposes |
| | 1081 | */ |
| | 1082 | if (err_cnt != 0) |
| | 1083 | G_tcmain->log_error(0, 0, TC_SEV_INTERNAL, |
| | 1084 | TCERR_UNRES_TMP_FIXUP, err_cnt); |
| | 1085 | } |
| | 1086 | |
| | 1087 | /* |
| | 1088 | * Allocate a new label at the current code offset |
| | 1089 | */ |
| | 1090 | CTcCodeLabel *CTcCodeStream::new_label_here() |
| | 1091 | { |
| | 1092 | CTcCodeLabel *lbl; |
| | 1093 | |
| | 1094 | /* allocate a new label */ |
| | 1095 | lbl = alloc_label(); |
| | 1096 | |
| | 1097 | /* set the label's location to the current write position */ |
| | 1098 | lbl->ofs = ofs_; |
| | 1099 | lbl->is_known = TRUE; |
| | 1100 | |
| | 1101 | /* return the new label */ |
| | 1102 | return lbl; |
| | 1103 | } |
| | 1104 | |
| | 1105 | /* |
| | 1106 | * Allocate a new forward-reference label |
| | 1107 | */ |
| | 1108 | CTcCodeLabel *CTcCodeStream::new_label_fwd() |
| | 1109 | { |
| | 1110 | CTcCodeLabel *lbl; |
| | 1111 | |
| | 1112 | /* allocate a new label */ |
| | 1113 | lbl = alloc_label(); |
| | 1114 | |
| | 1115 | /* the label's location is not yet known */ |
| | 1116 | lbl->ofs = 0; |
| | 1117 | lbl->is_known = FALSE; |
| | 1118 | |
| | 1119 | /* return the new label */ |
| | 1120 | return lbl; |
| | 1121 | } |
| | 1122 | |
| | 1123 | /* |
| | 1124 | * Define the position of a label, resolving any fixups associated with |
| | 1125 | * the label. |
| | 1126 | */ |
| | 1127 | void CTcCodeStream::def_label_pos(CTcCodeLabel *lbl) |
| | 1128 | { |
| | 1129 | /* set the label's position */ |
| | 1130 | lbl->ofs = ofs_; |
| | 1131 | lbl->is_known = TRUE; |
| | 1132 | |
| | 1133 | /* resolve each fixup */ |
| | 1134 | while (lbl->fhead != 0) |
| | 1135 | { |
| | 1136 | CTcLabelFixup *fixup; |
| | 1137 | long diff; |
| | 1138 | char buf[4]; |
| | 1139 | |
| | 1140 | /* pull this fixup off of the active list */ |
| | 1141 | fixup = lbl->fhead; |
| | 1142 | lbl->fhead = lbl->fhead->nxt; |
| | 1143 | |
| | 1144 | /* |
| | 1145 | * calculate the offset from the fixup position to the label |
| | 1146 | * position, applying the bias to the fixup position |
| | 1147 | */ |
| | 1148 | diff = lbl->ofs - (fixup->ofs + fixup->bias); |
| | 1149 | |
| | 1150 | /* convert the offset to the correct format and write it out */ |
| | 1151 | if (fixup->is_long) |
| | 1152 | { |
| | 1153 | /* write an INT4 offset value */ |
| | 1154 | oswp4(buf, diff); |
| | 1155 | write_at(fixup->ofs, buf, 4); |
| | 1156 | } |
| | 1157 | else |
| | 1158 | { |
| | 1159 | /* write an INT2 offset value */ |
| | 1160 | oswp2(buf, diff); |
| | 1161 | write_at(fixup->ofs, buf, 2); |
| | 1162 | } |
| | 1163 | |
| | 1164 | /* add this fixup to the free list, since we're finished with it */ |
| | 1165 | fixup->nxt = free_fixup_; |
| | 1166 | free_fixup_ = fixup; |
| | 1167 | } |
| | 1168 | } |
| | 1169 | |
| | 1170 | /* |
| | 1171 | * Determine if a label has a fixup at a particular offset |
| | 1172 | */ |
| | 1173 | int CTcCodeStream::has_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs) |
| | 1174 | { |
| | 1175 | CTcLabelFixup *fixup; |
| | 1176 | |
| | 1177 | /* scan for a match */ |
| | 1178 | for (fixup = lbl->fhead ; fixup != 0 ; fixup = fixup->nxt) |
| | 1179 | { |
| | 1180 | /* if this is a match, indicate success */ |
| | 1181 | if (fixup->ofs == ofs) |
| | 1182 | return TRUE; |
| | 1183 | } |
| | 1184 | |
| | 1185 | /* we didn't find a match */ |
| | 1186 | return FALSE; |
| | 1187 | } |
| | 1188 | |
| | 1189 | /* |
| | 1190 | * Remove a label's fixup at a particular offset |
| | 1191 | */ |
| | 1192 | void CTcCodeStream::remove_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs) |
| | 1193 | { |
| | 1194 | CTcLabelFixup *fixup; |
| | 1195 | CTcLabelFixup *prv; |
| | 1196 | CTcLabelFixup *nxt; |
| | 1197 | |
| | 1198 | /* scan for a match */ |
| | 1199 | for (prv = 0, fixup = lbl->fhead ; fixup != 0 ; prv = fixup, fixup = nxt) |
| | 1200 | { |
| | 1201 | /* remember the next one */ |
| | 1202 | nxt = fixup->nxt; |
| | 1203 | |
| | 1204 | /* if this is a match, remove it */ |
| | 1205 | if (fixup->ofs == ofs) |
| | 1206 | { |
| | 1207 | /* unlink this fixup from the list */ |
| | 1208 | if (prv == 0) |
| | 1209 | lbl->fhead = nxt; |
| | 1210 | else |
| | 1211 | prv->nxt = nxt; |
| | 1212 | |
| | 1213 | /* move it to the free list */ |
| | 1214 | fixup->nxt = free_fixup_; |
| | 1215 | free_fixup_ = fixup; |
| | 1216 | } |
| | 1217 | } |
| | 1218 | } |
| | 1219 | |
| | 1220 | /* |
| | 1221 | * Write an offset value to the given label |
| | 1222 | */ |
| | 1223 | void CTcCodeStream::write_ofs(CTcCodeLabel *lbl, int bias, int is_long) |
| | 1224 | { |
| | 1225 | /* if the label is known, write it; otherwise, generate a fixup */ |
| | 1226 | if (lbl->is_known) |
| | 1227 | { |
| | 1228 | long diff; |
| | 1229 | |
| | 1230 | /* |
| | 1231 | * calculate the branch offset from the current position, |
| | 1232 | * applying the bias to the current position |
| | 1233 | */ |
| | 1234 | diff = lbl->ofs - (ofs_ + bias); |
| | 1235 | |
| | 1236 | /* convert the offset to the correct format and write it out */ |
| | 1237 | if (is_long) |
| | 1238 | write4(diff); |
| | 1239 | else |
| | 1240 | write2(diff); |
| | 1241 | } |
| | 1242 | else |
| | 1243 | { |
| | 1244 | CTcLabelFixup *fixup; |
| | 1245 | |
| | 1246 | /* allocate a fixup */ |
| | 1247 | fixup = alloc_fixup(); |
| | 1248 | |
| | 1249 | /* set up the fixup data */ |
| | 1250 | fixup->ofs = ofs_; |
| | 1251 | fixup->bias = bias; |
| | 1252 | fixup->is_long = is_long; |
| | 1253 | |
| | 1254 | /* link the fixup into the label's fixup list */ |
| | 1255 | fixup->nxt = lbl->fhead; |
| | 1256 | lbl->fhead = fixup; |
| | 1257 | |
| | 1258 | /* write a placeholder to the code stream */ |
| | 1259 | if (is_long) |
| | 1260 | write4(0); |
| | 1261 | else |
| | 1262 | write2(0); |
| | 1263 | } |
| | 1264 | } |
| | 1265 | |
| | 1266 | /* |
| | 1267 | * Add a new line record at the current code offset |
| | 1268 | */ |
| | 1269 | void CTcCodeStream::add_line_rec(CTcTokFileDesc *file, long linenum) |
| | 1270 | { |
| | 1271 | tcgen_line_t *rec; |
| | 1272 | ulong cur_ofs; |
| | 1273 | int reuse; |
| | 1274 | |
| | 1275 | /* if there's no file descriptor, there's nothing to add */ |
| | 1276 | if (file == 0) |
| | 1277 | return; |
| | 1278 | |
| | 1279 | /* compute the current offset, relative to the start of the method */ |
| | 1280 | cur_ofs = G_cs->get_ofs() - method_ofs_; |
| | 1281 | |
| | 1282 | /* presume we won't re-use the previous record */ |
| | 1283 | reuse = FALSE; |
| | 1284 | |
| | 1285 | /* |
| | 1286 | * If we haven't added any code since the previous line record, |
| | 1287 | * overwrite the previous record - it doesn't refer to any |
| | 1288 | * executable code, so it's an unnecessary record. Similarly, if |
| | 1289 | * the previous record is at the same source position, don't add a |
| | 1290 | * separate line record for it, since the debugger won't be able to |
| | 1291 | * treat the two lines separately. |
| | 1292 | */ |
| | 1293 | if (line_cnt_ > 0) |
| | 1294 | { |
| | 1295 | /* get the previous record */ |
| | 1296 | rec = get_line_rec(line_cnt_ - 1); |
| | 1297 | |
| | 1298 | /* |
| | 1299 | * if it refers to the same code offset, replace the old record |
| | 1300 | * with one at this location |
| | 1301 | */ |
| | 1302 | if (rec->ofs == cur_ofs) |
| | 1303 | reuse = TRUE; |
| | 1304 | |
| | 1305 | /* |
| | 1306 | * if it has the identical source file location, don't bother |
| | 1307 | * adding a new record at all |
| | 1308 | */ |
| | 1309 | if (rec->source_id == file->get_index() |
| | 1310 | && rec->source_line == linenum) |
| | 1311 | return; |
| | 1312 | } |
| | 1313 | |
| | 1314 | /* if we're not re-using the previous record, allocate a new one */ |
| | 1315 | if (!reuse) |
| | 1316 | { |
| | 1317 | /* |
| | 1318 | * we need a new record - if we've used all the allocated space, |
| | 1319 | * allocate more |
| | 1320 | */ |
| | 1321 | if (line_cnt_ >= line_pages_alloc_ * TCGEN_LINE_PAGE_SIZE) |
| | 1322 | alloc_line_pages(5); |
| | 1323 | |
| | 1324 | /* get a pointer to the next available entry */ |
| | 1325 | rec = get_line_rec(line_cnt_); |
| | 1326 | |
| | 1327 | /* count the new record */ |
| | 1328 | ++line_cnt_; |
| | 1329 | } |
| | 1330 | |
| | 1331 | /* store the code offset relative to the start of the current method */ |
| | 1332 | rec->ofs = cur_ofs; |
| | 1333 | |
| | 1334 | /* store the file information */ |
| | 1335 | rec->source_id = file->get_index(); |
| | 1336 | rec->source_line = linenum; |
| | 1337 | |
| | 1338 | /* store the frame information */ |
| | 1339 | rec->frame = cur_frame_; |
| | 1340 | } |
| | 1341 | |
| | 1342 | /* |
| | 1343 | * Get the nth line record |
| | 1344 | */ |
| | 1345 | tcgen_line_t *CTcCodeStream::get_line_rec(size_t n) |
| | 1346 | { |
| | 1347 | return &(line_pages_[n / TCGEN_LINE_PAGE_SIZE] |
| | 1348 | ->lines[n % TCGEN_LINE_PAGE_SIZE]); |
| | 1349 | } |
| | 1350 | |
| | 1351 | /* |
| | 1352 | * Add a frame to the list of local frames in the method |
| | 1353 | */ |
| | 1354 | void CTcCodeStream::add_local_frame(CTcPrsSymtab *symtab) |
| | 1355 | { |
| | 1356 | /* |
| | 1357 | * If this is the global symbol table, or it's null, or it's already |
| | 1358 | * in a list, ignore it. Note that we can tell if the item is in a |
| | 1359 | * list by checking its index value - a value of zero is never a |
| | 1360 | * valid index and thus indicates that the item isn't in a list yet. |
| | 1361 | */ |
| | 1362 | if (symtab == G_prs->get_global_symtab() |
| | 1363 | || symtab == 0 |
| | 1364 | || symtab->get_list_index() != 0) |
| | 1365 | return; |
| | 1366 | |
| | 1367 | /* link the frame in at the tail of our list */ |
| | 1368 | symtab->set_list_next(0); |
| | 1369 | if (frame_tail_ == 0) |
| | 1370 | frame_head_ = symtab; |
| | 1371 | else |
| | 1372 | frame_tail_->set_list_next(symtab); |
| | 1373 | frame_tail_ = symtab; |
| | 1374 | |
| | 1375 | /* count the new entry in the list */ |
| | 1376 | ++frame_cnt_; |
| | 1377 | |
| | 1378 | /* |
| | 1379 | * Set this frame's index in the list. Note that we've already |
| | 1380 | * incremented the index value, so the first frame in the list will |
| | 1381 | * have index 1, as is required. |
| | 1382 | */ |
| | 1383 | symtab->set_list_index(frame_cnt_); |
| | 1384 | } |
| | 1385 | |
| | 1386 | /* ------------------------------------------------------------------------ */ |
| | 1387 | /* |
| | 1388 | * Data stream anchor |
| | 1389 | */ |
| | 1390 | |
| | 1391 | /* |
| | 1392 | * Get the length. We can deduce the length by subtracting our offset |
| | 1393 | * from the next item's offset, or, if we're the last item, from the |
| | 1394 | * length of the stream. |
| | 1395 | */ |
| | 1396 | ulong CTcStreamAnchor::get_len(CTcDataStream *ds) const |
| | 1397 | { |
| | 1398 | if (nxt_ != 0) |
| | 1399 | { |
| | 1400 | /* |
| | 1401 | * there's another item after me - my length is the difference |
| | 1402 | * between the next item's offset and my offset |
| | 1403 | */ |
| | 1404 | return (nxt_->ofs_ - ofs_); |
| | 1405 | } |
| | 1406 | else |
| | 1407 | { |
| | 1408 | /* I'm the last item - my length is whatever is left in the stream */ |
| | 1409 | return (ds->get_ofs() - ofs_); |
| | 1410 | } |
| | 1411 | } |
| | 1412 | |
| | 1413 | /* |
| | 1414 | * Set the finaly absoluate address, and apply fixups. The code |
| | 1415 | * generator must invoke this during the link phase once this object's |
| | 1416 | * final address is known. |
| | 1417 | */ |
| | 1418 | void CTcStreamAnchor::set_addr(ulong addr) |
| | 1419 | { |
| | 1420 | /* remember my address */ |
| | 1421 | addr_ = addr; |
| | 1422 | |
| | 1423 | /* apply all outstanding fixups for this object */ |
| | 1424 | CTcAbsFixup::fix_abs_fixup(*fixup_list_head_, addr); |
| | 1425 | } |
| | 1426 | |
| | 1427 | /* ------------------------------------------------------------------------ */ |
| | 1428 | /* |
| | 1429 | * Absolute Fixup Object |
| | 1430 | */ |
| | 1431 | |
| | 1432 | /* |
| | 1433 | * Add an absolute fixup at the current stream location to a given fixup |
| | 1434 | * list. |
| | 1435 | */ |
| | 1436 | void CTcAbsFixup::add_abs_fixup(CTcAbsFixup **list_head, |
| | 1437 | CTcDataStream *ds, ulong ofs) |
| | 1438 | { |
| | 1439 | CTcAbsFixup *fixup; |
| | 1440 | |
| | 1441 | /* |
| | 1442 | * create the fixup object - allocate it out of our fixup allocator |
| | 1443 | * pool, since this fixup object has the same attributes (small and |
| | 1444 | * long-lived) as other fixup objects |
| | 1445 | */ |
| | 1446 | fixup = (CTcAbsFixup *)G_prsmem->alloc(sizeof(CTcAbsFixup)); |
| | 1447 | |
| | 1448 | /* set the fixup location to the current offset */ |
| | 1449 | fixup->ds = ds; |
| | 1450 | fixup->ofs = ofs; |
| | 1451 | |
| | 1452 | /* link it in to the caller's list */ |
| | 1453 | fixup->nxt = *list_head; |
| | 1454 | *list_head = fixup; |
| | 1455 | } |
| | 1456 | |
| | 1457 | /* |
| | 1458 | * Fix up a fix-up list |
| | 1459 | */ |
| | 1460 | void CTcAbsFixup::fix_abs_fixup(CTcAbsFixup *list_head, ulong final_ofs) |
| | 1461 | { |
| | 1462 | CTcAbsFixup *cur; |
| | 1463 | |
| | 1464 | /* scan the list and fix up each entry */ |
| | 1465 | for (cur = list_head ; cur != 0 ; cur = cur->nxt) |
| | 1466 | { |
| | 1467 | /* |
| | 1468 | * fix this entry by writing the final offset in UINT4 format to |
| | 1469 | * the target stream at the target offset |
| | 1470 | */ |
| | 1471 | cur->ds->write4_at(cur->ofs, final_ofs); |
| | 1472 | } |
| | 1473 | } |
| | 1474 | |
| | 1475 | /* |
| | 1476 | * Write a fixup list to an object file |
| | 1477 | */ |
| | 1478 | void CTcAbsFixup:: |
| | 1479 | write_fixup_list_to_object_file(CVmFile *fp, CTcAbsFixup *list_head) |
| | 1480 | { |
| | 1481 | int cnt; |
| | 1482 | CTcAbsFixup *fixup; |
| | 1483 | |
| | 1484 | /* count the fixups */ |
| | 1485 | for (cnt = 0, fixup = list_head ; fixup != 0 ; |
| | 1486 | fixup = fixup->nxt, ++cnt) ; |
| | 1487 | |
| | 1488 | /* write the count */ |
| | 1489 | fp->write_int2(cnt); |
| | 1490 | |
| | 1491 | /* write the fixup list */ |
| | 1492 | for (fixup = list_head ; fixup != 0 ; fixup = fixup->nxt) |
| | 1493 | { |
| | 1494 | char buf[5]; |
| | 1495 | |
| | 1496 | /* write the data stream ID */ |
| | 1497 | buf[0] = fixup->ds->get_stream_id(); |
| | 1498 | |
| | 1499 | /* write the data stream offset of the reference */ |
| | 1500 | oswp4(buf+1, fixup->ofs); |
| | 1501 | |
| | 1502 | /* write the data */ |
| | 1503 | fp->write_bytes(buf, 5); |
| | 1504 | } |
| | 1505 | } |
| | 1506 | |
| | 1507 | /* |
| | 1508 | * Read a fixup list from an object file |
| | 1509 | */ |
| | 1510 | void CTcAbsFixup:: |
| | 1511 | load_fixup_list_from_object_file(CVmFile *fp, |
| | 1512 | const textchar_t *obj_fname, |
| | 1513 | CTcAbsFixup **list_head) |
| | 1514 | { |
| | 1515 | uint fixup_cnt; |
| | 1516 | |
| | 1517 | /* read the fixups */ |
| | 1518 | for (fixup_cnt = fp->read_uint2() ; fixup_cnt != 0 ; --fixup_cnt) |
| | 1519 | { |
| | 1520 | char buf[5]; |
| | 1521 | char stream_id; |
| | 1522 | ulong fixup_ofs; |
| | 1523 | CTcDataStream *stream; |
| | 1524 | |
| | 1525 | /* read the fixup data */ |
| | 1526 | fp->read_bytes(buf, 5); |
| | 1527 | stream_id = buf[0]; |
| | 1528 | fixup_ofs = t3rp4u(buf+1); |
| | 1529 | |
| | 1530 | /* find the stream for the ID */ |
| | 1531 | stream = CTcDataStream::get_stream_from_id(stream_id, obj_fname); |
| | 1532 | |
| | 1533 | /* if the stream is invalid, ignore this record */ |
| | 1534 | if (stream == 0) |
| | 1535 | continue; |
| | 1536 | |
| | 1537 | /* |
| | 1538 | * the fixup offset is relative to the starting offset of the |
| | 1539 | * stream for the current object file - adjust it accordingly |
| | 1540 | */ |
| | 1541 | fixup_ofs += stream->get_object_file_start_ofs(); |
| | 1542 | |
| | 1543 | /* create the fixup and add it to the list */ |
| | 1544 | add_abs_fixup(list_head, stream, fixup_ofs); |
| | 1545 | } |
| | 1546 | } |
| | 1547 | |
| | 1548 | /* ------------------------------------------------------------------------ */ |
| | 1549 | /* |
| | 1550 | * Object/property ID fixups |
| | 1551 | */ |
| | 1552 | |
| | 1553 | /* |
| | 1554 | * add a fixup to a list |
| | 1555 | */ |
| | 1556 | void CTcIdFixup::add_fixup(CTcIdFixup **list_head, class CTcDataStream *ds, |
| | 1557 | ulong ofs, ulong id) |
| | 1558 | { |
| | 1559 | CTcIdFixup *fixup; |
| | 1560 | |
| | 1561 | /* create the new fixup object */ |
| | 1562 | fixup = new (G_prsmem) CTcIdFixup(ds, ofs, id); |
| | 1563 | |
| | 1564 | /* link it in at the head of the list */ |
| | 1565 | fixup->nxt_ = *list_head; |
| | 1566 | *list_head = fixup; |
| | 1567 | } |
| | 1568 | |
| | 1569 | /* |
| | 1570 | * Apply this fixup |
| | 1571 | */ |
| | 1572 | void CTcIdFixup::apply_fixup(ulong final_id, size_t siz) |
| | 1573 | { |
| | 1574 | /* write the fixup */ |
| | 1575 | if (siz == 2) |
| | 1576 | ds_->write2_at(ofs_, (uint)final_id); |
| | 1577 | else |
| | 1578 | ds_->write4_at(ofs_, final_id); |
| | 1579 | } |
| | 1580 | |
| | 1581 | /* |
| | 1582 | * Write a fixup list to an object file |
| | 1583 | */ |
| | 1584 | void CTcIdFixup::write_to_object_file(CVmFile *fp, CTcIdFixup *head) |
| | 1585 | { |
| | 1586 | ulong cnt; |
| | 1587 | CTcIdFixup *cur; |
| | 1588 | |
| | 1589 | /* count the elements in the list */ |
| | 1590 | for (cur = head, cnt = 0 ; cur != 0 ; cur = cur->nxt_, ++cnt) ; |
| | 1591 | |
| | 1592 | /* write the count */ |
| | 1593 | fp->write_int4(cnt); |
| | 1594 | |
| | 1595 | /* write the fixups */ |
| | 1596 | for (cur = head ; cur != 0 ; cur = cur->nxt_) |
| | 1597 | { |
| | 1598 | char buf[9]; |
| | 1599 | |
| | 1600 | /* prepare a buffer with the data in portable format */ |
| | 1601 | buf[0] = cur->ds_->get_stream_id(); |
| | 1602 | oswp4(buf+1, cur->ofs_); |
| | 1603 | oswp4(buf+5, cur->id_); |
| | 1604 | |
| | 1605 | /* write the data */ |
| | 1606 | fp->write_bytes(buf, 9); |
| | 1607 | } |
| | 1608 | } |
| | 1609 | |
| | 1610 | /* |
| | 1611 | * Read an object ID fixup list from an object file |
| | 1612 | */ |
| | 1613 | void CTcIdFixup::load_object_file(CVmFile *fp, |
| | 1614 | const void *xlat, |
| | 1615 | ulong xlat_cnt, |
| | 1616 | tcgen_xlat_type xlat_type, |
| | 1617 | size_t stream_element_size, |
| | 1618 | const textchar_t *fname, |
| | 1619 | CTcIdFixup **fixup_list_head) |
| | 1620 | { |
| | 1621 | ulong cnt; |
| | 1622 | |
| | 1623 | /* read the count, then read the fixups */ |
| | 1624 | for (cnt = fp->read_uint4() ; cnt != 0 ; --cnt) |
| | 1625 | { |
| | 1626 | char buf[9]; |
| | 1627 | ulong ofs; |
| | 1628 | ulong old_id; |
| | 1629 | ulong new_id; |
| | 1630 | CTcDataStream *stream; |
| | 1631 | |
| | 1632 | /* read the next fixup */ |
| | 1633 | fp->read_bytes(buf, 9); |
| | 1634 | stream = CTcDataStream::get_stream_from_id(buf[0], fname); |
| | 1635 | ofs = t3rp4u(buf+1); |
| | 1636 | old_id = t3rp4u(buf+5); |
| | 1637 | |
| | 1638 | /* if the stream was invalid, ignore this record */ |
| | 1639 | if (stream == 0) |
| | 1640 | continue; |
| | 1641 | |
| | 1642 | /* adjust the offset for the stream's start in this object file */ |
| | 1643 | ofs += stream->get_object_file_start_ofs(); |
| | 1644 | |
| | 1645 | /* apply the fixup if a translation table was provided */ |
| | 1646 | if (xlat != 0) |
| | 1647 | { |
| | 1648 | /* make sure the count is in range - if it's not, ignore it */ |
| | 1649 | if (old_id >= xlat_cnt) |
| | 1650 | { |
| | 1651 | /* note the problem */ |
| | 1652 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 1653 | TCERR_OBJFILE_INVAL_OBJ_ID, fname); |
| | 1654 | |
| | 1655 | /* ignore the record */ |
| | 1656 | continue; |
| | 1657 | } |
| | 1658 | |
| | 1659 | /* look up the new ID */ |
| | 1660 | switch(xlat_type) |
| | 1661 | { |
| | 1662 | case TCGEN_XLAT_OBJ: |
| | 1663 | new_id = ((const tctarg_obj_id_t *)xlat)[old_id]; |
| | 1664 | break; |
| | 1665 | |
| | 1666 | case TCGEN_XLAT_PROP: |
| | 1667 | new_id = ((const tctarg_prop_id_t *)xlat)[old_id]; |
| | 1668 | break; |
| | 1669 | |
| | 1670 | case TCGEN_XLAT_ENUM: |
| | 1671 | new_id = ((const ulong *)xlat)[old_id]; |
| | 1672 | break; |
| | 1673 | } |
| | 1674 | |
| | 1675 | /* apply the fixup */ |
| | 1676 | if (stream_element_size == 2) |
| | 1677 | stream->write2_at(ofs, (uint)new_id); |
| | 1678 | else |
| | 1679 | stream->write4_at(ofs, new_id); |
| | 1680 | } |
| | 1681 | else |
| | 1682 | { |
| | 1683 | /* use the original ID for now */ |
| | 1684 | new_id = old_id; |
| | 1685 | } |
| | 1686 | |
| | 1687 | /* |
| | 1688 | * If we're keeping object fixups in the new file, create a |
| | 1689 | * fixup for the reference. Note that the fixup is for the new |
| | 1690 | * ID, not the old ID, because we've already translated the |
| | 1691 | * value in the stream to the new ID. |
| | 1692 | */ |
| | 1693 | if (fixup_list_head != 0) |
| | 1694 | CTcIdFixup::add_fixup(fixup_list_head, stream, ofs, new_id); |
| | 1695 | } |
| | 1696 | } |
| | 1697 | |