cfad47cfa3/t3compiler/tads3/tcgen.cpp
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 42.6 KB
(June 01, 2009 20:54 UTC) Almost 3 years ago
Initial commit.
Showing without highlighting since it looks like a big file and may slow your browser - show with highlighting
Show/hide line numbers#ifdef RCSID
static char RCSid[] =
"$Header: d:/cvsroot/tads/tads3/tcgen.cpp,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $";
#endif
/*
* Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
tcgen.cpp - TADS 3 Compiler code generator support classes
Function
Notes
Modified
05/09/99 MJRoberts - Creation
*/
#include <stdlib.h>
#include <string.h>
#include "t3std.h"
#include "os.h"
#include "tcglob.h"
#include "tcgen.h"
#include "vmerr.h"
#include "tcerrnum.h"
#include "tctok.h"
#include "tcprs.h"
#include "tcmain.h"
#include "vmfile.h"
#include "tctarg.h"
/* ------------------------------------------------------------------------ */
/*
* Data/Code Stream Parser-Allocated Object
*/
/*
* allocate via a parser memory allocator
*/
void *CTcCSPrsAllocObj::operator new(size_t siz, CTcPrsMem *allocator)
{
/* allocate via the allocator */
return allocator->alloc(siz);
}
/* ------------------------------------------------------------------------ */
/*
* Data Stream
*/
/*
* initialize
*/
CTcDataStream::CTcDataStream(char stream_id)
{
/* remember my ID */
stream_id_ = stream_id;
/* nothing is allocated yet */
ofs_ = 0;
obj_file_start_ofs_ = 0;
pages_ = 0;
page_slots_ = 0;
page_cnt_ = 0;
page_cur_ = 0;
rem_ = 0;
wp_ = 0;
/* we have no anchors yet */
first_anchor_ = last_anchor_ = 0;
/* create our parser memory allocator */
allocator_ = new CTcPrsMem();
}
/*
* delete
*/
CTcDataStream::~CTcDataStream()
{
size_t i;
/* delete the page slots if we allocated any */
for (i = 0 ; i < page_cnt_ ; ++i)
t3free(pages_[i]);
/* delete the page slot array if we allocated it */
if (pages_ != 0)
t3free(pages_);
/* delete our label/fixup allocator */
delete allocator_;
}
/*
* Reset
*/
void CTcDataStream::reset()
{
/* move the write pointer back to the start */
ofs_ = 0;
obj_file_start_ofs_ = 0;
/* back to the first page */
page_cur_ = 0;
/* set up to write to the first page, if we have any pages at all */
if (pages_ != 0)
{
/* we have all of the first page available again */
wp_ = calc_addr(0);
rem_ = TCCS_PAGE_SIZE;
}
/* reset the allocator */
allocator_->reset();
/*
* forget all of the anchors (no need to delete them explicitly -
* they were allocated from our allocator pool, which we've reset to
* completely discard everything it contained)
*/
first_anchor_ = last_anchor_ = 0;
}
/*
* Decrement the write offset
*/
void CTcDataStream::dec_ofs(int amount)
{
/* adjust the offset */
ofs_ -= amount;
/*
* calculate the new page we're on, since this may take us to a
* different page
*/
page_cur_ = ofs_ / TCCS_PAGE_SIZE;
/* calculate the remaining size in this page */
rem_ = TCCS_PAGE_SIZE - (ofs_ % TCCS_PAGE_SIZE);
/* calculate the current write pointer */
wp_ = calc_addr(ofs_);
}
/*
* Get a pointer to a block at a given offset and a given length.
*/
const char *CTcDataStream::get_block_ptr(ulong ofs,
ulong requested_len,
ulong *available_len)
{
size_t page_rem;
/*
* determine how much is left on the page containing the offset
* after the given offset
*/
page_rem = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE);
/*
* if the amount remaining on the page is greater than the request
* length, the available length is the entire request; otherwise,
* the available length is the amount remaining on the page
*/
if (page_rem >= requested_len)
*available_len = requested_len;
else
*available_len = page_rem;
/* return the address at this offset */
return calc_addr(ofs);
}
/*
* Write bytes to the stream at an earlier offset
*/
void CTcDataStream::write_at(ulong ofs, const char *buf, size_t len)
{
/* if we're writing to the current offset, use the normal writer */
if (ofs == ofs_)
write(buf, len);
/*
* log an internal error, and skip writing anything, if the desired
* range of offsets has not been previously written
*/
if (ofs + len > ofs_)
G_tok->throw_internal_error(TCERR_WRITEAT_PAST_END);
/* write the data to each page it spans */
while (len != 0)
{
size_t cur;
/*
* determine how much is left on the page containing the current
* starting offset
*/
cur = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE);
/*
* figure out how much we can copy - copy the whole remaining
* size, but no more than the amount remaining on this page
*/
if (cur > len)
cur = len;
/* copy the data */
memcpy(calc_addr(ofs), buf, cur);
/* advance past this chunk */
len -= cur;
ofs += cur;
buf += cur;
}
}
/*
* Copy a chunk of the stream to the given buffer
*/
void CTcDataStream::copy_to_buf(char *buf, ulong start_ofs, ulong len)
{
/* read the data from each page that the block spans */
while (len != 0)
{
size_t cur;
/*
* determine how much is left on the page containing the current
* starting offset
*/
cur = TCCS_PAGE_SIZE - (start_ofs % TCCS_PAGE_SIZE);
/*
* figure out how much we can copy - copy the whole remaining
* size, but no more than the amount remaining on this page
*/
if (cur > len)
cur = (size_t)len;
/* copy the data */
memcpy(buf, calc_addr(start_ofs), cur);
/* advance past this chunk */
len -= cur;
start_ofs += cur;
buf += cur;
}
}
/*
* Reserve space
*/
ulong CTcDataStream::reserve(size_t len)
{
ulong ret;
/* we'll always return the offset current before the call */
ret = ofs_;
/* if we have space on the current page, it's easy */
if (len <= rem_)
{
/* advance the output pointers */
ofs_ += len;
wp_ += len;
rem_ -= len;
}
else
{
/* keep going until we satisfy the request */
do
{
size_t cur;
/* if necessary, allocate more memory */
if (rem_ == 0)
alloc_page();
/* limit this chunk to the space remaining on the current page */
cur = len;
if (cur > rem_)
cur = rem_;
/* skip past this chunk */
ofs_ += cur;
wp_ += cur;
rem_ -= cur;
len -= cur;
} while (len != 0);
}
/* return the starting offset */
return ret;
}
/*
* Append data from another stream. The source stream is permanently
* moved to the new stream, destroying the original stream.
*/
void CTcDataStream::append_stream(CTcDataStream *stream)
{
ulong rem;
ulong ofs;
ulong start_ofs;
CTcStreamAnchor *anchor;
CTcStreamAnchor *nxt;
/* remember the starting offset of the copy in my stream */
start_ofs = get_ofs();
/* copy all data from the other stream */
for (ofs = 0, rem = stream->get_ofs() ; rem != 0 ; )
{
ulong request;
const char *ptr;
ulong actual;
/*
* request as much as possible from the other stream, up to the
* remaining length or 64k, whichever is smaller
*/
request = 65535;
if (rem < request)
request = rem;
/* get the chunk from the source stream */
ptr = stream->get_block_ptr(ofs, request, &actual);
/*
* write this chunk (which we know is less than 64k and can thus
* be safely cast to size_t, even on 16-bit machines)
*/
write(ptr, (size_t)actual);
/* advance our counters */
rem -= actual;
ofs += actual;
}
/*
* Now copy all of the anchors from the source stream to our stream.
* This will ensure that fixups in the other stream have
* corresponding fixups in this stream. Note that we must adjust
* the offset of each copied anchor by the offset of the start of
* the copied data in our stream.
*/
for (anchor = stream->get_first_anchor() ; anchor != 0 ; anchor = nxt)
{
/*
* remember the old link to the next anchor, since we're going
* to move the anchor to my list and thus forget about its
* position in the old list
*/
nxt = anchor->nxt_;
/* adjust the anchor's offset */
anchor->ofs_ += start_ofs;
/* unlink the anchor from its old stream */
anchor->nxt_ = 0;
/* link it in to my anchor list */
if (last_anchor_ != 0)
last_anchor_->nxt_ = anchor;
else
first_anchor_ = anchor;
last_anchor_ = anchor;
}
}
/*
* Write bytes to the stream
*/
void CTcDataStream::write(const char *buf, size_t len)
{
/*
* if possible, write it in one go (this is for efficiency, so that
* we can avoid making a few comparisons in the most common case)
*/
if (len <= rem_)
{
/* write the data */
memcpy(wp_, buf, len);
/* advance the output pointers */
ofs_ += len;
wp_ += len;
rem_ -= len;
}
else
{
/* keep going until we satisfy the request */
do
{
size_t cur;
/* if necessary, allocate more memory */
if (rem_ == 0)
alloc_page();
/* limit this chunk to the space remaining on the current page */
cur = len;
if (cur > rem_)
cur = rem_;
/* copy it to the page */
memcpy(wp_, buf, cur);
/* skip past the space written in the destination */
ofs_ += cur;
wp_ += cur;
rem_ -= cur;
/* advance past the space in the source */
buf += cur;
len -= cur;
} while (len != 0);
}
}
/*
* allocate a new page
*/
void CTcDataStream::alloc_page()
{
/*
* if we're coming back to a page that was previously allocated, we
* need merely re-establish the existing page
*/
if (page_cur_ + 1 < page_cnt_)
{
/* move to the next page */
++page_cur_;
/* start writing at the start of the page */
wp_ = pages_[page_cur_];
rem_ = TCCS_PAGE_SIZE;
/* we're done */
return;
}
/*
* if we don't have room for a new page in the page array, expand
* the page array
*/
if (page_cnt_ >= page_slots_)
{
/* increase the page slot count */
page_slots_ += 100;
/* allocate or reallocate the page array */
if (pages_ == 0)
pages_ = (char **)t3malloc(page_slots_ * sizeof(pages_[0]));
else
pages_ = (char **)t3realloc(pages_,
page_slots_ * sizeof(pages_[0]));
/* if that failed, throw an error */
if (pages_ == 0)
err_throw(TCERR_CODEGEN_NO_MEM);
}
/* allocate the new page */
pages_[page_cnt_] = (char *)t3malloc(TCCS_PAGE_SIZE);
/* throw an error if we couldn't allocate the page */
if (pages_[page_cnt_] == 0)
err_throw(TCERR_CODEGEN_NO_MEM);
/* start writing at the start of the new page */
wp_ = pages_[page_cnt_];
/* the entire page is free */
rem_ = TCCS_PAGE_SIZE;
/* make the new page the current page */
page_cur_ = page_cnt_;
/* count the new page */
++page_cnt_;
}
/*
* Add an absolute fixup for this stream at the current write offset.
*/
void CTcDataStream::add_abs_fixup(CTcAbsFixup **list_head)
{
/* add the fixup to the list at my current write location */
CTcAbsFixup::add_abs_fixup(list_head, this, get_ofs());
}
/*
* Add an anchor at the current offset.
*/
CTcStreamAnchor *CTcDataStream::add_anchor(CTcSymbol *owner_sym,
CTcAbsFixup **fixup_list_head,
ulong ofs)
{
CTcStreamAnchor *anchor;
/* allocate the anchor, giving it our current offset */
anchor = new (allocator_) CTcStreamAnchor(owner_sym,
fixup_list_head, ofs);
/* append it to our list */
if (last_anchor_ != 0)
last_anchor_->nxt_ = anchor;
else
first_anchor_ = anchor;
last_anchor_ = anchor;
/* return the new anchor */
return anchor;
}
/*
* Find an anchor with the given stream offset
*/
CTcStreamAnchor *CTcDataStream::find_anchor(ulong ofs) const
{
CTcStreamAnchor *cur;
/* scan the anchor list */
for (cur = first_anchor_ ; cur != 0 ; cur = cur->nxt_)
{
/* if this one has the desired offset, return it */
if (cur->get_ofs() == ofs)
return cur;
}
/* didn't find it */
return 0;
}
/*
* Write an object ID
*/
void CTcDataStream::write_obj_id(ulong obj_id)
{
/*
* if there's an object ID fixup list, and this is a valid object
* reference (not a 'nil' reference), add this reference
*/
if (G_keep_objfixups && obj_id != TCTARG_INVALID_OBJ)
CTcIdFixup::add_fixup(&G_objfixup, this, get_ofs(), obj_id);
/* write the ID */
write4(obj_id);
}
/*
* Write an object ID self-reference
*/
void CTcDataStream::write_obj_id_selfref(CTcSymObj *obj_sym)
{
/*
* Add a fixup list entry to the symbol. This type of reference
* must be kept with the symbol rather than in the global list,
* because we must apply this type of fixup each time we renumber
* the symbol.
*/
obj_sym->add_self_ref_fixup(this, get_ofs());
/* write the ID to the stream */
write4(obj_sym->get_obj_id());
}
/*
* Write a property ID
*/
void CTcDataStream::write_prop_id(uint prop_id)
{
/* if there's an object ID fixup list, add this reference */
if (G_keep_propfixups)
CTcIdFixup::add_fixup(&G_propfixup, this, get_ofs(), prop_id);
/* write the ID */
write2(prop_id);
}
/*
* Write an enumerator ID
*/
void CTcDataStream::write_enum_id(ulong enum_id)
{
/* if there's a fixup list, add this reference */
if (G_keep_enumfixups)
CTcIdFixup::add_fixup(&G_enumfixup, this, get_ofs(), enum_id);
/* write the ID */
write4(enum_id);
}
/*
* Write the stream, its anchors, and its fixups to an object file.
*/
void CTcDataStream::write_to_object_file(CVmFile *fp)
{
ulong ofs;
CTcStreamAnchor *anchor;
long cnt;
/*
* First, write the data stream bytes. Write the length prefix
* followed by the data. Just blast the whole thing out in one huge
* byte stream, one page at a time.
*/
fp->write_int4(ofs_);
/* write the data one page at a time */
for (ofs = 0 ; ofs < ofs_ ; )
{
size_t cur;
/*
* write out one whole page, or the balance of the current page
* if we have less than a whole page remaining
*/
cur = TCCS_PAGE_SIZE;
if (ofs + cur > ofs_)
cur = (size_t)(ofs_ - ofs);
/* write out this chunk */
fp->write_bytes(calc_addr(ofs), cur);
/* move to the next page's offset */
ofs += cur;
}
/* count the anchors */
for (cnt = 0, anchor = first_anchor_ ; anchor != 0 ;
anchor = anchor->nxt_, ++cnt) ;
/* write the count */
fp->write_int4(cnt);
/*
* Write all of the anchors, and all of their fixups. (We have the
* code to write the anchor and fixup information in-line here for
* efficiency - there will normally be a large number of these tiny
* objects, so anything we can do to improve the speed of this loop
* will help quite a lot in the overall write performance.)
*/
for (anchor = first_anchor_ ; anchor != 0 ; anchor = anchor->nxt_)
{
char buf[6];
/* write the stream offset */
oswp4(buf, anchor->get_ofs());
/*
* If the anchor has an external fixup list, write the symbol's
* name to the file; otherwise write a zero length to indicate
* that we have an internal fixup list.
*/
if (anchor->get_fixup_owner_sym() == 0)
{
/* no external list - indicate with a zero symbol length */
oswp2(buf+4, 0);
fp->write_bytes(buf, 6);
}
else
{
/* external list - write the symbol length and name */
oswp2(buf+4, anchor->get_fixup_owner_sym()->get_sym_len());
fp->write_bytes(buf, 6);
fp->write_bytes(anchor->get_fixup_owner_sym()->get_sym(),
anchor->get_fixup_owner_sym()->get_sym_len());
}
/* write the fixup list */
CTcAbsFixup::
write_fixup_list_to_object_file(fp, *anchor->fixup_list_head_);
}
}
/*
* Read a stream from an object file
*/
void CTcDataStream::load_object_file(CVmFile *fp,
const textchar_t *fname)
{
ulong stream_len;
ulong rem;
char buf[1024];
ulong start_ofs;
ulong anchor_cnt;
/* read the length of the stream */
stream_len = fp->read_uint4();
/* remember my starting offset */
start_ofs = get_ofs();
/* read the stream bytes */
for (rem = stream_len ; rem != 0 ; )
{
size_t cur;
/* read up to a buffer-full, or however much is left */
cur = sizeof(buf);
if (cur > rem)
cur = rem;
/* read this chunk */
fp->read_bytes(buf, cur);
/* add this chunk to the stream */
write(buf, cur);
/* deduct the amount we've read from the amount remaining */
rem -= cur;
}
/*
* Read the anchors. For each anchor, we must fix up the anchor by
* adding the base address of the stream we just read - the original
* anchor offsets in the object file reflect a base stream offset of
* zero, but we could be loading the stream after a bunch of other
* data have already been loaded into the stream.
*
* First, read the number of anchors, then loop through the anchors
* and read each one.
*/
for (anchor_cnt = fp->read_uint4() ; anchor_cnt != 0 ;
--anchor_cnt)
{
ulong anchor_ofs;
size_t sym_len;
CTcStreamAnchor *anchor;
/* read this anchor */
fp->read_bytes(buf, 6);
/* get the offset, and adjust for the new stream base offset */
anchor_ofs = t3rp4u(buf) + start_ofs;
/* get the length of the owning symbol's name, if any */
sym_len = osrp2(buf+4);
/* if there's a symbol name, read it */
if (sym_len != 0)
{
CTcSymbol *owner_sym;
/* read the symbol name */
fp->read_bytes(buf, sym_len);
/* look it up in the global symbol table */
owner_sym = G_prs->get_global_symtab()->find(buf, sym_len);
if (owner_sym == 0)
{
/*
* the owner symbol doesn't exist - this is an internal
* inconsistency in the object file, because the anchor
* symbol must always be defined in the same file and
* hence should have been loaded already; complain and
* go on
*/
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
TCERR_OBJFILE_INT_SYM_MISSING,
(int)sym_len, buf, fname);
/* we can't create the anchor */
anchor = 0;
}
else
{
/* create the anchor based on the symbol */
anchor = add_anchor(owner_sym,
owner_sym->get_fixup_list_anchor(),
anchor_ofs);
/* set the anchor in the symbol */
owner_sym->set_anchor(anchor);
}
}
else
{
/* create the anchor with no external references */
anchor = add_anchor(0, 0, anchor_ofs);
}
/* load the fixup list */
CTcAbsFixup::
load_fixup_list_from_object_file(fp, fname,
anchor->fixup_list_head_);
}
}
/*
* Given a stream ID, get the stream
*/
CTcDataStream *CTcDataStream::
get_stream_from_id(char stream_id, const textchar_t *obj_fname)
{
switch(stream_id)
{
case TCGEN_DATA_STREAM:
return G_ds;
case TCGEN_CODE_STREAM:
return G_cs_main;
case TCGEN_STATIC_CODE_STREAM:
return G_cs_static;
case TCGEN_OBJECT_STREAM:
return G_os;
case TCGEN_ICMOD_STREAM:
return G_icmod_stream;
case TCGEN_BIGNUM_STREAM:
return G_bignum_stream;
case TCGEN_STATIC_INIT_ID_STREAM:
return G_static_init_id_stream;
default:
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
TCERR_OBJFILE_INVAL_STREAM_ID, obj_fname);
return 0;
}
}
/* ------------------------------------------------------------------------ */
/*
* Code Stream
*/
/*
* create the code stream
*/
CTcCodeStream::CTcCodeStream(char stream_id)
: CTcDataStream(stream_id)
{
/* no switch yet */
cur_switch_ = 0;
/* no enclosing statement yet */
enclosing_ = 0;
/* no code body being generated yet */
code_body_ = 0;
/* start writing at offset zero */
ofs_ = 0;
/* no symbol tables yet */
symtab_ = 0;
goto_symtab_ = 0;
/* no labels yet */
active_lbl_ = 0;
free_lbl_ = 0;
/* no fixups yet */
free_fixup_ = 0;
/* allocate an initial set of line record pages */
line_pages_alloc_ = 0;
line_pages_ = 0;
alloc_line_pages(5);
/* no line records in use yet */
line_cnt_ = 0;
}
/*
* destroy the code stream
*/
CTcCodeStream::~CTcCodeStream()
{
size_t i;
/* release all active labels */
release_labels();
/* delete the line records pages */
for (i = 0 ; i < line_pages_alloc_ ; ++i)
t3free(line_pages_[i]);
/* delete the master list of pages */
t3free(line_pages_);
}
/*
* Reset
*/
void CTcCodeStream::reset()
{
/* inherit default */
CTcDataStream::reset();
/* clear the line records */
clear_line_recs();
/*
* forget all of the labels and fixups - they're allocated from the
* allocator pool, which we've completely reset now
*/
free_lbl_ = active_lbl_ = 0;
free_fixup_ = 0;
/* forget all of the symbol tables */
symtab_ = 0;
goto_symtab_ = 0;
/* forget the frame list */
frame_head_ = frame_tail_ = 0;
cur_frame_ = 0;
frame_cnt_ = 0;
/* forget all of the statement settings */
cur_switch_ = 0;
enclosing_ = 0;
code_body_ = 0;
/* presume 'self' is not available */
self_available_ = FALSE;
}
/*
* Allocate line record pages
*/
void CTcCodeStream::alloc_line_pages(size_t number_to_add)
{
size_t siz;
size_t i;
/* create or expand the master page array */
siz = (line_pages_alloc_ + number_to_add) * sizeof(tcgen_line_page_t *);
if (line_pages_ == 0)
line_pages_ = (tcgen_line_page_t **)t3malloc(siz);
else
line_pages_ = (tcgen_line_page_t **)t3realloc(line_pages_, siz);
/* allocate the new pages */
for (i = line_pages_alloc_ ; i < line_pages_alloc_ + number_to_add ; ++i)
{
/* allocate this page */
line_pages_[i] = (tcgen_line_page_t *)
t3malloc(sizeof(tcgen_line_page_t));
}
/* remember the new allocation */
line_pages_alloc_ += number_to_add;
}
/*
* Allocate a new label object
*/
CTcCodeLabel *CTcCodeStream::alloc_label()
{
CTcCodeLabel *ret;
/* if there's anything in the free list, use it */
if (free_lbl_ != 0)
{
/* take the first one off the free list */
ret = free_lbl_;
/* unlink it from the list */
free_lbl_ = free_lbl_->nxt;
}
else
{
/* allocate a new label */
ret = new (allocator_) CTcCodeLabel;
/* throw an error if allocation failed */
if (ret == 0)
err_throw(TCERR_CODEGEN_NO_MEM);
}
/* add the label to the active list */
ret->nxt = active_lbl_;
active_lbl_ = ret;
/* return the allocated label */
return ret;
}
/*
* Allocate a new fixup object
*/
CTcLabelFixup *CTcCodeStream::alloc_fixup()
{
CTcLabelFixup *ret;
/* if there's anything in the free list, use it */
if (free_fixup_ != 0)
{
/* take the first one off the free list */
ret = free_fixup_;
/* unlink it from the list */
free_fixup_ = free_fixup_->nxt;
}
else
{
/* allocate a new fixup */
ret = new (allocator_) CTcLabelFixup;
/* throw an error if allocation failed */
if (ret == 0)
err_throw(TCERR_CODEGEN_NO_MEM);
}
/* return the allocated fixup */
return ret;
}
/*
* Release all active labels. If any labels are undefined, log an
* internal error.
*/
void CTcCodeStream::release_labels()
{
int err_cnt;
/* we haven't found any errors yet */
err_cnt = 0;
/* run through the list of active labels */
while (active_lbl_ != 0)
{
CTcCodeLabel *lbl;
/* pull this label off of the active list */
lbl = active_lbl_;
active_lbl_ = active_lbl_->nxt;
/* put this label on the free list */
lbl->nxt = free_lbl_;
free_lbl_ = lbl;
/* check for unresolved fixups */
while (lbl->fhead != 0)
{
CTcLabelFixup *fixup;
/* pull this fixup off of the active list */
fixup = lbl->fhead;
lbl->fhead = lbl->fhead->nxt;
/* put this fixup on the free list */
fixup->nxt = free_fixup_;
free_fixup_ = fixup;
/* count the unresolved label */
++err_cnt;
}
}
/*
* if we found any unresolved fixups, log the error; there's not
* much point in logging each error individually, since this is an
* internal compiler error that the user can't do anything about,
* but at least give the user a count for compiler diagnostic
* purposes
*/
if (err_cnt != 0)
G_tcmain->log_error(0, 0, TC_SEV_INTERNAL,
TCERR_UNRES_TMP_FIXUP, err_cnt);
}
/*
* Allocate a new label at the current code offset
*/
CTcCodeLabel *CTcCodeStream::new_label_here()
{
CTcCodeLabel *lbl;
/* allocate a new label */
lbl = alloc_label();
/* set the label's location to the current write position */
lbl->ofs = ofs_;
lbl->is_known = TRUE;
/* return the new label */
return lbl;
}
/*
* Allocate a new forward-reference label
*/
CTcCodeLabel *CTcCodeStream::new_label_fwd()
{
CTcCodeLabel *lbl;
/* allocate a new label */
lbl = alloc_label();
/* the label's location is not yet known */
lbl->ofs = 0;
lbl->is_known = FALSE;
/* return the new label */
return lbl;
}
/*
* Define the position of a label, resolving any fixups associated with
* the label.
*/
void CTcCodeStream::def_label_pos(CTcCodeLabel *lbl)
{
/* set the label's position */
lbl->ofs = ofs_;
lbl->is_known = TRUE;
/* resolve each fixup */
while (lbl->fhead != 0)
{
CTcLabelFixup *fixup;
long diff;
char buf[4];
/* pull this fixup off of the active list */
fixup = lbl->fhead;
lbl->fhead = lbl->fhead->nxt;
/*
* calculate the offset from the fixup position to the label
* position, applying the bias to the fixup position
*/
diff = lbl->ofs - (fixup->ofs + fixup->bias);
/* convert the offset to the correct format and write it out */
if (fixup->is_long)
{
/* write an INT4 offset value */
oswp4(buf, diff);
write_at(fixup->ofs, buf, 4);
}
else
{
/* write an INT2 offset value */
oswp2(buf, diff);
write_at(fixup->ofs, buf, 2);
}
/* add this fixup to the free list, since we're finished with it */
fixup->nxt = free_fixup_;
free_fixup_ = fixup;
}
}
/*
* Determine if a label has a fixup at a particular offset
*/
int CTcCodeStream::has_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs)
{
CTcLabelFixup *fixup;
/* scan for a match */
for (fixup = lbl->fhead ; fixup != 0 ; fixup = fixup->nxt)
{
/* if this is a match, indicate success */
if (fixup->ofs == ofs)
return TRUE;
}
/* we didn't find a match */
return FALSE;
}
/*
* Remove a label's fixup at a particular offset
*/
void CTcCodeStream::remove_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs)
{
CTcLabelFixup *fixup;
CTcLabelFixup *prv;
CTcLabelFixup *nxt;
/* scan for a match */
for (prv = 0, fixup = lbl->fhead ; fixup != 0 ; prv = fixup, fixup = nxt)
{
/* remember the next one */
nxt = fixup->nxt;
/* if this is a match, remove it */
if (fixup->ofs == ofs)
{
/* unlink this fixup from the list */
if (prv == 0)
lbl->fhead = nxt;
else
prv->nxt = nxt;
/* move it to the free list */
fixup->nxt = free_fixup_;
free_fixup_ = fixup;
}
}
}
/*
* Write an offset value to the given label
*/
void CTcCodeStream::write_ofs(CTcCodeLabel *lbl, int bias, int is_long)
{
/* if the label is known, write it; otherwise, generate a fixup */
if (lbl->is_known)
{
long diff;
/*
* calculate the branch offset from the current position,
* applying the bias to the current position
*/
diff = lbl->ofs - (ofs_ + bias);
/* convert the offset to the correct format and write it out */
if (is_long)
write4(diff);
else
write2(diff);
}
else
{
CTcLabelFixup *fixup;
/* allocate a fixup */
fixup = alloc_fixup();
/* set up the fixup data */
fixup->ofs = ofs_;
fixup->bias = bias;
fixup->is_long = is_long;
/* link the fixup into the label's fixup list */
fixup->nxt = lbl->fhead;
lbl->fhead = fixup;
/* write a placeholder to the code stream */
if (is_long)
write4(0);
else
write2(0);
}
}
/*
* Add a new line record at the current code offset
*/
void CTcCodeStream::add_line_rec(CTcTokFileDesc *file, long linenum)
{
tcgen_line_t *rec;
ulong cur_ofs;
int reuse;
/* if there's no file descriptor, there's nothing to add */
if (file == 0)
return;
/* compute the current offset, relative to the start of the method */
cur_ofs = G_cs->get_ofs() - method_ofs_;
/* presume we won't re-use the previous record */
reuse = FALSE;
/*
* If we haven't added any code since the previous line record,
* overwrite the previous record - it doesn't refer to any
* executable code, so it's an unnecessary record. Similarly, if
* the previous record is at the same source position, don't add a
* separate line record for it, since the debugger won't be able to
* treat the two lines separately.
*/
if (line_cnt_ > 0)
{
/* get the previous record */
rec = get_line_rec(line_cnt_ - 1);
/*
* if it refers to the same code offset, replace the old record
* with one at this location
*/
if (rec->ofs == cur_ofs)
reuse = TRUE;
/*
* if it has the identical source file location, don't bother
* adding a new record at all
*/
if (rec->source_id == file->get_index()
&& rec->source_line == linenum)
return;
}
/* if we're not re-using the previous record, allocate a new one */
if (!reuse)
{
/*
* we need a new record - if we've used all the allocated space,
* allocate more
*/
if (line_cnt_ >= line_pages_alloc_ * TCGEN_LINE_PAGE_SIZE)
alloc_line_pages(5);
/* get a pointer to the next available entry */
rec = get_line_rec(line_cnt_);
/* count the new record */
++line_cnt_;
}
/* store the code offset relative to the start of the current method */
rec->ofs = cur_ofs;
/* store the file information */
rec->source_id = file->get_index();
rec->source_line = linenum;
/* store the frame information */
rec->frame = cur_frame_;
}
/*
* Get the nth line record
*/
tcgen_line_t *CTcCodeStream::get_line_rec(size_t n)
{
return &(line_pages_[n / TCGEN_LINE_PAGE_SIZE]
->lines[n % TCGEN_LINE_PAGE_SIZE]);
}
/*
* Add a frame to the list of local frames in the method
*/
void CTcCodeStream::add_local_frame(CTcPrsSymtab *symtab)
{
/*
* If this is the global symbol table, or it's null, or it's already
* in a list, ignore it. Note that we can tell if the item is in a
* list by checking its index value - a value of zero is never a
* valid index and thus indicates that the item isn't in a list yet.
*/
if (symtab == G_prs->get_global_symtab()
|| symtab == 0
|| symtab->get_list_index() != 0)
return;
/* link the frame in at the tail of our list */
symtab->set_list_next(0);
if (frame_tail_ == 0)
frame_head_ = symtab;
else
frame_tail_->set_list_next(symtab);
frame_tail_ = symtab;
/* count the new entry in the list */
++frame_cnt_;
/*
* Set this frame's index in the list. Note that we've already
* incremented the index value, so the first frame in the list will
* have index 1, as is required.
*/
symtab->set_list_index(frame_cnt_);
}
/* ------------------------------------------------------------------------ */
/*
* Data stream anchor
*/
/*
* Get the length. We can deduce the length by subtracting our offset
* from the next item's offset, or, if we're the last item, from the
* length of the stream.
*/
ulong CTcStreamAnchor::get_len(CTcDataStream *ds) const
{
if (nxt_ != 0)
{
/*
* there's another item after me - my length is the difference
* between the next item's offset and my offset
*/
return (nxt_->ofs_ - ofs_);
}
else
{
/* I'm the last item - my length is whatever is left in the stream */
return (ds->get_ofs() - ofs_);
}
}
/*
* Set the finaly absoluate address, and apply fixups. The code
* generator must invoke this during the link phase once this object's
* final address is known.
*/
void CTcStreamAnchor::set_addr(ulong addr)
{
/* remember my address */
addr_ = addr;
/* apply all outstanding fixups for this object */
CTcAbsFixup::fix_abs_fixup(*fixup_list_head_, addr);
}
/* ------------------------------------------------------------------------ */
/*
* Absolute Fixup Object
*/
/*
* Add an absolute fixup at the current stream location to a given fixup
* list.
*/
void CTcAbsFixup::add_abs_fixup(CTcAbsFixup **list_head,
CTcDataStream *ds, ulong ofs)
{
CTcAbsFixup *fixup;
/*
* create the fixup object - allocate it out of our fixup allocator
* pool, since this fixup object has the same attributes (small and
* long-lived) as other fixup objects
*/
fixup = (CTcAbsFixup *)G_prsmem->alloc(sizeof(CTcAbsFixup));
/* set the fixup location to the current offset */
fixup->ds = ds;
fixup->ofs = ofs;
/* link it in to the caller's list */
fixup->nxt = *list_head;
*list_head = fixup;
}
/*
* Fix up a fix-up list
*/
void CTcAbsFixup::fix_abs_fixup(CTcAbsFixup *list_head, ulong final_ofs)
{
CTcAbsFixup *cur;
/* scan the list and fix up each entry */
for (cur = list_head ; cur != 0 ; cur = cur->nxt)
{
/*
* fix this entry by writing the final offset in UINT4 format to
* the target stream at the target offset
*/
cur->ds->write4_at(cur->ofs, final_ofs);
}
}
/*
* Write a fixup list to an object file
*/
void CTcAbsFixup::
write_fixup_list_to_object_file(CVmFile *fp, CTcAbsFixup *list_head)
{
int cnt;
CTcAbsFixup *fixup;
/* count the fixups */
for (cnt = 0, fixup = list_head ; fixup != 0 ;
fixup = fixup->nxt, ++cnt) ;
/* write the count */
fp->write_int2(cnt);
/* write the fixup list */
for (fixup = list_head ; fixup != 0 ; fixup = fixup->nxt)
{
char buf[5];
/* write the data stream ID */
buf[0] = fixup->ds->get_stream_id();
/* write the data stream offset of the reference */
oswp4(buf+1, fixup->ofs);
/* write the data */
fp->write_bytes(buf, 5);
}
}
/*
* Read a fixup list from an object file
*/
void CTcAbsFixup::
load_fixup_list_from_object_file(CVmFile *fp,
const textchar_t *obj_fname,
CTcAbsFixup **list_head)
{
uint fixup_cnt;
/* read the fixups */
for (fixup_cnt = fp->read_uint2() ; fixup_cnt != 0 ; --fixup_cnt)
{
char buf[5];
char stream_id;
ulong fixup_ofs;
CTcDataStream *stream;
/* read the fixup data */
fp->read_bytes(buf, 5);
stream_id = buf[0];
fixup_ofs = t3rp4u(buf+1);
/* find the stream for the ID */
stream = CTcDataStream::get_stream_from_id(stream_id, obj_fname);
/* if the stream is invalid, ignore this record */
if (stream == 0)
continue;
/*
* the fixup offset is relative to the starting offset of the
* stream for the current object file - adjust it accordingly
*/
fixup_ofs += stream->get_object_file_start_ofs();
/* create the fixup and add it to the list */
add_abs_fixup(list_head, stream, fixup_ofs);
}
}
/* ------------------------------------------------------------------------ */
/*
* Object/property ID fixups
*/
/*
* add a fixup to a list
*/
void CTcIdFixup::add_fixup(CTcIdFixup **list_head, class CTcDataStream *ds,
ulong ofs, ulong id)
{
CTcIdFixup *fixup;
/* create the new fixup object */
fixup = new (G_prsmem) CTcIdFixup(ds, ofs, id);
/* link it in at the head of the list */
fixup->nxt_ = *list_head;
*list_head = fixup;
}
/*
* Apply this fixup
*/
void CTcIdFixup::apply_fixup(ulong final_id, size_t siz)
{
/* write the fixup */
if (siz == 2)
ds_->write2_at(ofs_, (uint)final_id);
else
ds_->write4_at(ofs_, final_id);
}
/*
* Write a fixup list to an object file
*/
void CTcIdFixup::write_to_object_file(CVmFile *fp, CTcIdFixup *head)
{
ulong cnt;
CTcIdFixup *cur;
/* count the elements in the list */
for (cur = head, cnt = 0 ; cur != 0 ; cur = cur->nxt_, ++cnt) ;
/* write the count */
fp->write_int4(cnt);
/* write the fixups */
for (cur = head ; cur != 0 ; cur = cur->nxt_)
{
char buf[9];
/* prepare a buffer with the data in portable format */
buf[0] = cur->ds_->get_stream_id();
oswp4(buf+1, cur->ofs_);
oswp4(buf+5, cur->id_);
/* write the data */
fp->write_bytes(buf, 9);
}
}
/*
* Read an object ID fixup list from an object file
*/
void CTcIdFixup::load_object_file(CVmFile *fp,
const void *xlat,
ulong xlat_cnt,
tcgen_xlat_type xlat_type,
size_t stream_element_size,
const textchar_t *fname,
CTcIdFixup **fixup_list_head)
{
ulong cnt;
/* read the count, then read the fixups */
for (cnt = fp->read_uint4() ; cnt != 0 ; --cnt)
{
char buf[9];
ulong ofs;
ulong old_id;
ulong new_id;
CTcDataStream *stream;
/* read the next fixup */
fp->read_bytes(buf, 9);
stream = CTcDataStream::get_stream_from_id(buf[0], fname);
ofs = t3rp4u(buf+1);
old_id = t3rp4u(buf+5);
/* if the stream was invalid, ignore this record */
if (stream == 0)
continue;
/* adjust the offset for the stream's start in this object file */
ofs += stream->get_object_file_start_ofs();
/* apply the fixup if a translation table was provided */
if (xlat != 0)
{
/* make sure the count is in range - if it's not, ignore it */
if (old_id >= xlat_cnt)
{
/* note the problem */
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
TCERR_OBJFILE_INVAL_OBJ_ID, fname);
/* ignore the record */
continue;
}
/* look up the new ID */
switch(xlat_type)
{
case TCGEN_XLAT_OBJ:
new_id = ((const tctarg_obj_id_t *)xlat)[old_id];
break;
case TCGEN_XLAT_PROP:
new_id = ((const tctarg_prop_id_t *)xlat)[old_id];
break;
case TCGEN_XLAT_ENUM:
new_id = ((const ulong *)xlat)[old_id];
break;
}
/* apply the fixup */
if (stream_element_size == 2)
stream->write2_at(ofs, (uint)new_id);
else
stream->write4_at(ofs, new_id);
}
else
{
/* use the original ID for now */
new_id = old_id;
}
/*
* If we're keeping object fixups in the new file, create a
* fixup for the reference. Note that the fixup is for the new
* ID, not the old ID, because we've already translated the
* value in the stream to the new ID.
*/
if (fixup_list_head != 0)
CTcIdFixup::add_fixup(fixup_list_head, stream, ofs, new_id);
}
}
|