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