cfad47cfa3/t3compiler/tads3/tcgen.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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
     */
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 */
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 */
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);
161
    void write_at(ulong ofs, char b) { write_at(ofs, &b, 1); }
162
163
    /* write a TADS portable UINT2 value */
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 */
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 */
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 */
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 */
229
    char get_byte_at(ulong ofs)
230
    {
231
        return *calc_addr(ofs);
232
    }
233
234
    /* read an INT2 value at the given offst */
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 */
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 */
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
    /* read a UINT4 value at the given offset */
270
    uint readu4_at(ulong ofs)
271
    {
272
        char buf[4];
273
274
        /* read the four bytes */
275
        buf[0] = get_byte_at(ofs);
276
        buf[1] = get_byte_at(ofs + 1);
277
        buf[2] = get_byte_at(ofs + 2);
278
        buf[3] = get_byte_at(ofs + 3);
279
        return t3rp4u(buf);
280
    }
281
282
    /*
283
     *   Add an absolute fixup at the current stream location to a given
284
     *   absolute fixup list.  We'll create a new fixup object, record our
285
     *   current offset in the fixup so that we come back and fix this
286
     *   location, and add the fixup object to the given list.  
287
     */
288
    void add_abs_fixup(struct CTcAbsFixup **list_head);
289
290
    /* get the head of my list of anchors */
291
    struct CTcStreamAnchor *get_first_anchor()
292
        const { return first_anchor_; }
293
    
294
    /* 
295
     *   Add an anchor at the current offset.  This should be used each
296
     *   time an atomic item is written to the stream.
297
     *   
298
     *   If the fixup list head pointer is not null, we will store fixups
299
     *   for the anchor in the given list.  Otherwise, we'll provide an
300
     *   internal list head in the anchor object.  Objects that are
301
     *   reachable through multiple references (such as an object
302
     *   associated with a symbol table entry) must typically keep their
303
     *   own fixup lists, because the fixup list might be needed before
304
     *   and after the data stream object is created.  Objects that can
305
     *   only be reached through a single reference, which is created only
306
     *   when the data stream object is created, can use the internal
307
     *   fixup list instead.
308
     *   
309
     *   If an external fixup list is provided, the owning symbol table
310
     *   entry must be provided.  This is necessary so that, when writing
311
     *   the anchor to an object file, we can also store a reference to
312
     *   the symbol that owns the list; this allows the association to be
313
     *   restored when the object file is loaded.  
314
     */
315
    struct CTcStreamAnchor *add_anchor(class CTcSymbol *owner_sym,
316
                                       struct CTcAbsFixup **fixup_list_head)
317
    {
318
        return add_anchor(owner_sym, fixup_list_head, get_ofs());
319
    }
320
321
    /* add an anchor at the given offset */
322
    struct CTcStreamAnchor *add_anchor(class CTcSymbol *owner_sym,
323
                                       struct CTcAbsFixup **fixup_list_head,
324
                                       ulong ofs);
325
326
    /* find an anchor at the given offset */
327
    struct CTcStreamAnchor *find_anchor(ulong ofs) const;
328
329
    /*
330
     *   Write the stream to an object file.  Writes the stream data and
331
     *   the list of anchors and their fixups.  Every fixup should be
332
     *   reachable from an anchor, and all of the anchors are in our
333
     *   anchor list.  
334
     */
335
    void write_to_object_file(class CVmFile *fp);
336
337
    /*
338
     *   Load an object file 
339
     */
340
    void load_object_file(class CVmFile *fp, const textchar_t *obj_fname);
341
342
    /*
343
     *   Get/set the object file starting offset - this is the base offset
344
     *   of the stream for the current object.  Because streams can refer
345
     *   to one another, the object file loader must set the starting
346
     *   offset for all streams before reading the first stream for an
347
     *   object file.  The 'set' call simply sets the starting offset to
348
     *   the current offset; this won't be affected by subsequent loading
349
     *   into the stream, so we have a stable base address for the object
350
     *   file data in the stream. 
351
     */
352
    ulong get_object_file_start_ofs() const { return obj_file_start_ofs_; }
353
    void set_object_file_start_ofs() { obj_file_start_ofs_ = ofs_; }
354
355
    /*
356
     *   Reset - discard all data in the stream 
357
     */
358
    virtual void reset();
359
360
protected:
361
    /* allocate a new page */
362
    void alloc_page();
363
364
    /* 
365
     *   Calculate the memory address of a given byte, given the byte's
366
     *   offset.  The caller must be sure that the offset is less than the
367
     *   current write position, since this routine does not allocate new
368
     *   memory.  
369
     */
370
    char *calc_addr(ulong ofs) const
371
    {
372
        /* 
373
         *   get the page number by dividing the offset by the page size;
374
         *   get the offset in the page from the remainder of the same
375
         *   division 
376
         */
377
        return pages_[ofs / TCCS_PAGE_SIZE] + (ofs % TCCS_PAGE_SIZE);
378
    }
379
380
    /* current write offset */
381
    ulong ofs_;
382
383
    /* start offset of the stream for the current object file's data */
384
    ulong obj_file_start_ofs_;
385
386
    /* current write pointer */
387
    char *wp_;
388
389
    /* space remaining on the current page */
390
    size_t rem_;
391
392
    /* current page */
393
    size_t page_cur_;
394
395
    /* array of pages */
396
    char **pages_;
397
398
    /* number of page slots */
399
    size_t page_slots_;
400
401
    /* number of pages allocated */
402
    size_t page_cnt_;
403
404
    /* head and tail of my list of anchors */
405
    struct CTcStreamAnchor *first_anchor_;
406
    struct CTcStreamAnchor *last_anchor_;
407
408
    /* 
409
     *   parser memory allocator - we use this for allocating label and
410
     *   fixup objects; these objects fit the characteristics used for the
411
     *   parser memory allocator, so we can get better memory management
412
     *   efficiency by using this allocator 
413
     */
414
    class CTcPrsMem *allocator_;
415
416
    /* my stream ID, for identification in the object file */
417
    char stream_id_;
418
};
419
420
/* ------------------------------------------------------------------------ */
421
/*
422
 *   Debugging line record.  This record stores information on one
423
 *   executable line of source code, associating the byte-code location of
424
 *   the code with the source file ID and line number, plus the frame
425
 *   identifier (which gives the local scope in effect for the line of
426
 *   code).  
427
 */
428
struct tcgen_line_t
429
{
430
    /* 
431
     *   Byte-code offset of the first instruction for this source line.
432
     *   This is expressed as an offset from the start of the method
433
     *   header, which means that this value is relative and thus doesn't
434
     *   need any adjustment for linking or any other changes to the
435
     *   absolute location where the code is stored.  
436
     */
437
    uint ofs;
438
439
    /* 
440
     *   Source file ID and line number.  The source file ID is the index
441
     *   of the file descriptor in the tokenizer's master source file
442
     *   list, so it must be adjusted when multiple object files are
443
     *   linked together for the fact that the file descriptor indices can
444
     *   change.  
445
     */
446
    int source_id;
447
    long source_line;
448
449
    /* 
450
     *   frame - this is the local scope frame for this line, which
451
     *   specifies the local symbol table in effect for the line of code 
452
     */
453
    class CTcPrsSymtab *frame;
454
};
455
456
/* number of line records per page */
457
const size_t TCGEN_LINE_PAGE_SIZE = 1024;
458
459
/*
460
 *   Debugging line record page.  We allocate blocks of line records for
461
 *   memory efficiency; this is a page of line numbers. 
462
 */
463
struct tcgen_line_page_t
464
{
465
    /* underlying line records */
466
    tcgen_line_t lines[TCGEN_LINE_PAGE_SIZE];
467
};
468
469
470
/* ------------------------------------------------------------------------ */
471
/*
472
 *   Code Stream.  This object provides a place for code generators to
473
 *   store byte code.  A code stream is an extension of a byte stream,
474
 *   with support for symbol table access; enclosing switch, loop, and
475
 *   'try' block tracking; forward-reference labels; and relative jump
476
 *   generation.
477
 */
478
479
/* code stream class */
480
class CTcCodeStream: public CTcDataStream
481
{
482
public:
483
    CTcCodeStream(char stream_id);
484
    ~CTcCodeStream();
485
486
    /*
487
     *   Temporary label operations.  A temporary label can be used to
488
     *   generate forward or reverse jumps.
489
     */
490
491
    /* allocate a new label at the current write offset */
492
    struct CTcCodeLabel *new_label_here();
493
494
    /* 
495
     *   allocate a forward-reference label; the position will be defined
496
     *   later via the def_label_pos() method 
497
     */
498
    struct CTcCodeLabel *new_label_fwd();
499
500
    /* 
501
     *   define the position of a label allocated as a forward reference
502
     *   to be the current write offset 
503
     */
504
    void def_label_pos(struct CTcCodeLabel *lbl);
505
506
    /*
507
     *   Remove a fixup at a particular location for a label.  This can be
508
     *   used if an instruction is being removed (for optimization, for
509
     *   example).  
510
     */
511
    void remove_fixup_at_ofs(struct CTcCodeLabel *lbl, ulong ofs);
512
513
    /*
514
     *   Check a label's fixup list to see if it has a fixup at a
515
     *   particular code stream offset.  Returns true if there is such a
516
     *   fixup, false if not. 
517
     */
518
    int has_fixup_at_ofs(struct CTcCodeLabel *lbl, ulong ofs);
519
520
    /* 
521
     *   Release all active labels.  Logs an internal error if any labels
522
     *   are still forward-declared. 
523
     */
524
    void release_labels();
525
526
    /*
527
     *   Get the current enclosing statement.  This is used to find the
528
     *   target of a 'break' or 'continue', and is also used to invoke
529
     *   'finally' blocks when leaving a nested block. 
530
     */
531
    class CTPNStmEnclosing *get_enclosing() const { return enclosing_; }
532
533
    /*
534
     *   Set the enclosing statement.  During code generation, each time
535
     *   an enclosing statement is encountered, it should be set as the
536
     *   current enclosing statement for the duration of its code
537
     *   generation, so that its subnodes can find it.  This routine
538
     *   returns the previous enclosing statement so that it can be
539
     *   restored later.  
540
     */
541
    class CTPNStmEnclosing *set_enclosing(CTPNStmEnclosing *stm)
542
    {
543
        CTPNStmEnclosing *old_stm;
544
545
        /* save the old one */
546
        old_stm = enclosing_;
547
548
        /* set the new one */
549
        enclosing_ = stm;
550
551
        /* return the old one */
552
        return old_stm;
553
    }
554
555
    /*
556
     *   Set the current code body 
557
     */
558
    class CTPNCodeBody *set_code_body(CTPNCodeBody *cb)
559
    {
560
        CTPNCodeBody *old_cb;
561
562
        /* save the old one */
563
        old_cb = code_body_;
564
565
        /* set the new one */
566
        code_body_ = cb;
567
568
        /* return the old one */
569
        return old_cb;
570
    }
571
572
    /* get the current code body being generated */
573
    class CTPNCodeBody *get_code_body() const { return code_body_; }
574
575
    /* 
576
     *   Write the relative offset to the given label as a 2- or 4-byte
577
     *   value in TADS portable INT2 or INT4 format.  The 'bias' value
578
     *   gives the offset from the current write pointer of the source
579
     *   position; the relative value written is thus:
580
     *   
581
     *   (label - (current + bias))
582
     */
583
    void write_ofs2(struct CTcCodeLabel *lbl, int bias)
584
        { write_ofs(lbl, bias, FALSE); }
585
    void write_ofs4(struct CTcCodeLabel *lbl, int bias)
586
        { write_ofs(lbl, bias, TRUE); }
587
588
    /*
589
     *   Set the enclosing "switch" statement node, returning the previous
590
     *   one to allow for later restoration 
591
     */
592
    class CTPNStmSwitch *set_switch(class CTPNStmSwitch *sw)
593
    {
594
        class CTPNStmSwitch *old_sw;
595
596
        /* remember the current switch node for a moment */
597
        old_sw = cur_switch_;
598
599
        /* set the new switch */
600
        cur_switch_ = sw;
601
602
        /* return the previous one */
603
        return old_sw;
604
    }
605
606
    /* get the current switch */
607
    class CTPNStmSwitch *get_switch() const { return cur_switch_; }
608
609
    /* get the active symbol table */
610
    class CTcPrsSymtab *get_symtab() const { return symtab_; }
611
612
    /* get the active 'goto' symbol table */
613
    class CTcPrsSymtab *get_goto_symtab() const { return goto_symtab_; }
614
615
    /* set the active symbol table */
616
    void set_symtab(class CTcPrsSymtab *symtab) { symtab_ = symtab; }
617
618
    /* set the active 'goto' symbol table */
619
    void set_goto_symtab(class CTcPrsSymtab *symtab)
620
        { goto_symtab_ = symtab; }
621
622
    /* 
623
     *   get/self 'self' availability - if we're generating code for a
624
     *   stand-alone function, 'self' is not available; if we're
625
     *   generating code for an object method, 'self' is available 
626
     */
627
    int is_self_available() const { return self_available_; }
628
    void set_self_available(int f) { self_available_ = f; }
629
630
    /* add a debugging line record at the current byte-code offset */
631
    void add_line_rec(class CTcTokFileDesc *file, long linenum);
632
633
    /* clear all line records */
634
    void clear_line_recs() { line_cnt_ = 0; }
635
636
    /* get the number of line records */
637
    size_t get_line_rec_count() const { return line_cnt_; }
638
639
    /* get the nth line record */
640
    struct tcgen_line_t *get_line_rec(size_t n);
641
642
    /* set the starting offset of the current method */
643
    void set_method_ofs(ulong ofs) { method_ofs_ = ofs; }
644
645
    /* clear the list of local frames in the current method */
646
    void clear_local_frames()
647
    {
648
        /* clear the list */
649
        frame_head_ = frame_tail_ = 0;
650
651
        /* reset the count */
652
        frame_cnt_ = 0;
653
    }
654
655
    /* 
656
     *   Set the current local frame.  This will add the frame to the
657
     *   master list of frames for the current method if it's not already
658
     *   there, and will establish the frame as the current local frame.
659
     *   Returns the previous local frame, so that the caller can restore
660
     *   the enclosing frame when leaving a nested frame.  
661
     */
662
    class CTcPrsSymtab *set_local_frame(class CTcPrsSymtab *symtab)
663
    {
664
        class CTcPrsSymtab *old_frame;
665
666
        /* remember the original local frame, so we can return it later */
667
        old_frame = cur_frame_;
668
        
669
        /* remember the current frame */
670
        cur_frame_ = symtab;
671
672
        /* add it to the local frame list for the method if necessary */
673
        add_local_frame(symtab);
674
675
        /* return the original local frame */
676
        return old_frame;
677
    }
678
679
    /* get the local frame count for this method */
680
    size_t get_frame_count() const { return frame_cnt_; }
681
682
    /* get the head of the frame list for the method */
683
    class CTcPrsSymtab *get_first_frame() const { return frame_head_; }
684
685
    /* reset */
686
    virtual void reset();
687
688
protected:
689
    /* 
690
     *   add a frame to the list of local frames; does nothing if the
691
     *   frame is already in the list for this method 
692
     */
693
    void add_local_frame(class CTcPrsSymtab *symtab);
694
695
    /* allocate additional line record pages */
696
    void alloc_line_pages(size_t number_to_add);
697
698
    /* allocate a label object */
699
    struct CTcCodeLabel *alloc_label();
700
    
701
    /* allocate a fixup object */
702
    struct CTcLabelFixup *alloc_fixup();
703
704
    /* 
705
     *   Write an offset to the given label.  If is_long is true, we'll
706
     *   write an INT4 offset; otherwise we'll write an INT2 offset value. 
707
     */
708
    void write_ofs(struct CTcCodeLabel *lbl, int bias, int is_long);
709
710
    /* head of list of active temporary labels */
711
    struct CTcCodeLabel *active_lbl_;
712
713
    /* head of list of free temporary label objects */
714
    struct CTcCodeLabel *free_lbl_;
715
716
    /* head of list of free label fixup objects */
717
    struct CTcLabelFixup *free_fixup_;
718
719
    /* current symbol table */
720
    class CTcPrsSymtab *symtab_;
721
722
    /* current 'goto' symbol table */
723
    class CTcPrsSymtab *goto_symtab_;
724
725
    /* current "switch" statement */
726
    class CTPNStmSwitch *cur_switch_;
727
728
    /* current enclosing statement */
729
    class CTPNStmEnclosing *enclosing_;
730
731
    /* current code body being generated */
732
    class CTPNCodeBody *code_body_;
733
734
    /* flag: 'self' is available in current code body */
735
    unsigned int self_available_ : 1;
736
737
    /* array of line record pages */
738
    tcgen_line_page_t **line_pages_;
739
    size_t line_pages_alloc_;
740
741
    /* number of line records actually used */
742
    size_t line_cnt_;
743
744
    /* starting offst of current method */
745
    ulong method_ofs_;
746
747
    /* head and tail of list of local frames for the current method */
748
    class CTcPrsSymtab *frame_head_;
749
    class CTcPrsSymtab *frame_tail_;
750
751
    /* number of frames in the local method list so far */
752
    size_t frame_cnt_;
753
754
    /* currently active local frame */
755
    class CTcPrsSymtab *cur_frame_;
756
};
757
758
/* ------------------------------------------------------------------------ */
759
/*
760
 *   Code Stream Parser-Allocated Object - this is a base class for
761
 *   objects allocated by a CTcPrsMem allocator. 
762
 */
763
struct CTcCSPrsAllocObj
764
{
765
    /* allocate via the parser allocator */
766
    void *operator new(size_t siz, class CTcPrsMem *allocator);
767
};
768
769
/* ------------------------------------------------------------------------ */
770
/*
771
 *   Code label 
772
 */
773
struct CTcCodeLabel: CTcCSPrsAllocObj
774
{
775
    CTcCodeLabel()
776
    {
777
        /* clear members */
778
        nxt = 0;
779
        ofs = 0;
780
        fhead = 0;
781
        is_known = FALSE;
782
    }
783
784
    /* next code label */
785
    CTcCodeLabel *nxt;
786
787
    /* offset of the code associated with the label */
788
    ulong ofs;
789
790
    /* 
791
     *   head of list of fixups for this label; this will always be null
792
     *   once the label's offset is known, since we will resolve any
793
     *   existing fixups as soon as the label is defined and will add no
794
     *   further fixups after it's known, since we can generate correct
795
     *   offsets directly 
796
     */
797
    struct CTcLabelFixup *fhead;
798
799
    /* flag: true -> offset is known */
800
    uint is_known : 1;
801
};
802
803
/*
804
 *   Code label fixup.  Each time we generate a jump to a code label that
805
 *   hasn't been defined yet, we'll generate a fixup and attach it to the
806
 *   label.  The fixup records the location of the jump; as soon as the
807
 *   label is defined, we'll go through all of the fixup records
808
 *   associated with the label, and fill in the correct jump offset.  
809
 */
810
struct CTcLabelFixup: CTcCSPrsAllocObj
811
{
812
    CTcLabelFixup()
813
    {
814
        /* clear members */
815
        nxt = 0;
816
        ofs = 0;
817
        bias = 0;
818
        is_long = FALSE;
819
    }
820
    
821
    /* next fixup in same list */
822
    CTcLabelFixup *nxt;
823
824
    /* code offset of the jump in need of fixing */
825
    ulong ofs;
826
827
    /* 
828
     *   bias to apply to jump offset (the value we will write is:
829
     *   
830
     *   (target - (ofs + bias)) 
831
     */
832
    int bias;
833
834
    /* long jump - true -> INT4 offset, false -> INT2 offset */
835
    uint is_long : 1;
836
};
837
838
839
/* ------------------------------------------------------------------------ */
840
/*
841
 *   Absolute Fixup.  Each time we generate a reference to an offset in a
842
 *   stream, we must save the location so that we can go back and fix up
843
 *   the offset after the final image file layout configuration.  We can't
844
 *   know until we've generated the entire program what the final location
845
 *   of any stream is, because we won't know how large we're going to make
846
 *   the pool pages until we've generated the whole program.
847
 *   
848
 *   Each absolute fixup is stored in a list anchored in the object that
849
 *   owns the object in the stream to which the fixup refers.  During the
850
 *   image layout configuration phase, we go through the list of all pool
851
 *   blocks, and figure out where the pool block will go in the image
852
 *   file; once this is known, we apply all of the fixups for that
853
 *   block by scanning the block's fixup list.  
854
 */
855
struct CTcAbsFixup: CTcCSPrsAllocObj
856
{
857
    /* next fixup in same list */
858
    CTcAbsFixup *nxt;
859
860
    /* stream containing the reference */
861
    class CTcDataStream *ds;
862
    
863
    /* location in stream 'ds' of the 4-byte pointer value to fix */
864
    ulong ofs;
865
866
    /*
867
     *   Add an absolute fixup, at a given offset in a given stream, to a
868
     *   given list.  
869
     */
870
    static void add_abs_fixup(struct CTcAbsFixup **list_head,
871
                              CTcDataStream *ds, ulong ofs);
872
873
    /*
874
     *   Fix up a fix-up list, given the final address of the referenced
875
     *   object.  Scans the fix-up list and stores the final address at
876
     *   each location in the list.  
877
     */
878
    static void fix_abs_fixup(struct CTcAbsFixup *list_head,
879
                              ulong final_ofs);
880
881
    /* write a fixup list to an object file */
882
    static void write_fixup_list_to_object_file(class CVmFile *fp,
883
                                                CTcAbsFixup *list_head);
884
885
    /* load a fixup list from an object file */
886
    static void
887
        load_fixup_list_from_object_file(class CVmFile *fp,
888
                                         const textchar_t *obj_fname,
889
                                         CTcAbsFixup **list_head);
890
891
};
892
893
/* ------------------------------------------------------------------------ */
894
/*
895
 *   ID Fixup.  We use this to track a reference to an object ID or
896
 *   property ID.  These fixups are needed when combining object files
897
 *   that were compiled separately, since we must reconcile the ID name
898
 *   spaces of the multiple files.  
899
 */
900
901
/* translation datatypes */
902
enum tcgen_xlat_type
903
{
904
    /* object ID's - translation table type is (tctarg_obj_id_t *) */
905
    TCGEN_XLAT_OBJ,
906
907
    /* property ID's - translation table type is (tctarg_prop_id_t *) */
908
    TCGEN_XLAT_PROP,
909
910
    /* enum's - translation table type is (ulong *) */
911
    TCGEN_XLAT_ENUM
912
};
913
914
/*
915
 *   ID fixup class 
916
 */
917
struct CTcIdFixup: CTcCSPrsAllocObj
918
{
919
    /* initialize */
920
    CTcIdFixup(class CTcDataStream *ds, ulong ofs, ulong id)
921
    {
922
        /* remember the data */
923
        ds_ = ds;
924
        ofs_ = ofs;
925
        id_ = id;
926
927
        /* we're not in a list yet */
928
        nxt_ = 0;
929
    }
930
    
931
    /* add a fixup to a fixup list */
932
    static void add_fixup(CTcIdFixup **list_head, class CTcDataStream *ds,
933
                          ulong ofs, ulong id);
934
935
    /* write a fixup list to an object file */
936
    static void write_to_object_file(class CVmFile *fp, CTcIdFixup *head);
937
938
    /* 
939
     *   Load an ID fixup list from an object file.
940
     *   
941
     *   'xlat' is the translation array; it consists of elements of the
942
     *   appropriate type, depending on xlat_type.
943
     *   
944
     *   'xlat_cnt' is the number of elements in the 'xlat' array.
945
     *   
946
     *   'stream_element_size' is the size of the stream data elements
947
     *   that we're fixing up.  This can be 2 for a UINT2 value, or 4 for
948
     *   a UINT4 value.
949
     *   
950
     *   'fname' is the object filename, which we need for reporting
951
     *   errors that we encounter in the fixup data.
952
     *   
953
     *   If 'fixup_list_head' is provided, it points to the head of a list
954
     *   that we use to store new fixups.  For each fixup we read, we
955
     *   create a new fixup referring to the same element.  This is needed
956
     *   if the file we're reading will be turned back into another object
957
     *   file at some point and needs relative object ID information to be
958
     *   stored.  
959
     */
960
    static void load_object_file(class CVmFile *fp,
961
                                 const void *xlat, ulong xlat_cnt,
962
                                 tcgen_xlat_type xlat_type,
963
                                 size_t stream_element_size,
964
                                 const textchar_t *fname,
965
                                 CTcIdFixup **fixup_list_head);
966
967
    /* 
968
     *   apply the fixup, given the final ID to use, and the size of the
969
     *   data item to write (2 for a UINT2, 4 for a UINT4) 
970
     */
971
    void apply_fixup(ulong final_id, size_t siz);
972
973
    /* the stream containing the reference to this object */
974
    class CTcDataStream *ds_;
975
976
    /* the offset in the stream of the reference */
977
    ulong ofs_;
978
979
    /* 
980
     *   the local ID - this is the ID that is used locally in the
981
     *   separate file before it's translated to the final value global to
982
     *   the combined files 
983
     */
984
    ulong id_;
985
986
    /* next fixup in the list */
987
    CTcIdFixup *nxt_;
988
};
989
990
991
/* ------------------------------------------------------------------------ */
992
/*
993
 *   Data Stream Anchor.  Each time we add an object to a data stream, we
994
 *   must add an anchor to the stream.  The anchor tracks the indivisible
995
 *   object and all references (via a fixup list) to the object.  
996
 */
997
struct CTcStreamAnchor: CTcCSPrsAllocObj
998
{
999
    CTcStreamAnchor(class CTcSymbol *fixup_owner_sym,
1000
                    struct CTcAbsFixup **fixup_list_head, ulong stream_ofs)
1001
    {
1002
        /* we're not in a list yet */
1003
        nxt_ = 0;
1004
1005
        /* 
1006
         *   if the caller provided an external fixup list head pointer,
1007
         *   use it; otherwise, use our internal fixup list 
1008
         */
1009
        if (fixup_list_head != 0)
1010
        {
1011
            /* use the external fixup list */
1012
            fixup_list_head_ = fixup_list_head;
1013
1014
            /* remember the owning symbol */
1015
            fixup_info_.fixup_owner_sym_ = fixup_owner_sym;
1016
        }
1017
        else
1018
        {
1019
            /* use our internal fixup list */
1020
            fixup_list_head_ = &fixup_info_.internal_fixup_head_;
1021
1022
            /* we have no fixup items in our internal list yet */
1023
            fixup_info_.internal_fixup_head_ = 0;
1024
        }
1025
1026
        /* remember the stream offset */
1027
        ofs_ = stream_ofs;
1028
1029
        /* our pool address is not yet known */
1030
        addr_ = 0;
1031
1032
        /* we're not yet replaced */
1033
        replaced_ = FALSE;
1034
    }
1035
1036
    /* 
1037
     *   Detach from our symbol, switching to our internal fixup list.  We
1038
     *   leave any fixups that were previously associated with our symbol
1039
     *   with the symbol, so this detaches us from any fixups currently
1040
     *   pointing to me.  This is useful for replacing and modifying symbols,
1041
     *   since it allows the anchor for the original definition to be
1042
     *   dissociated from the symbol, so that the symbol can be reused with a
1043
     *   different anchor.  
1044
     */
1045
    void detach_from_symbol()
1046
    {
1047
        /* switch to our internal fixup list */
1048
        fixup_list_head_ = &fixup_info_.internal_fixup_head_;
1049
1050
        /* we have nothing in our list yet */
1051
        fixup_info_.internal_fixup_head_ = 0;
1052
    }
1053
1054
    /* get my offset - this is the stream offset, not the final address */
1055
    ulong get_ofs() const { return ofs_; }
1056
1057
    /* 
1058
     *   Get my final address - valid only after the linking phase (as
1059
     *   defined and performed by the target-specific code generator).
1060
     *   The exact meaning of this address is defined by the
1061
     *   target-specific code generator, and this value is meant for use
1062
     *   exclusively by the target code generator; the target-independent
1063
     *   part of the compiler should have no use for this value.  
1064
     */
1065
    ulong get_addr() const { return addr_; }
1066
1067
    /* 
1068
     *   Set the address - this should be invoked by the target-specific
1069
     *   code generator during the link phase when the final address of
1070
     *   the stream object is known.  This will store the address in the
1071
     *   anchor, and will apply all of the outstanding fixups for the
1072
     *   object.  
1073
     */
1074
    void set_addr(ulong addr);
1075
    
1076
    /* 
1077
     *   Get the length.  We don't separately track the length, because
1078
     *   the length can be deduced from the offset of the next item (or
1079
     *   from the length of the stream, when the item is the last item in
1080
     *   the stream).  
1081
     */
1082
    ulong get_len(class CTcDataStream *ds) const;
1083
1084
    /*
1085
     *   Get the symbol table entry that owns my fixup list, if the fixup
1086
     *   list is external.  Returns null if the fixup list is internal. 
1087
     */
1088
    class CTcSymbol *get_fixup_owner_sym() const
1089
    {
1090
        return (fixup_list_head_ == &fixup_info_.internal_fixup_head_
1091
                ? 0 : fixup_info_.fixup_owner_sym_);
1092
    }
1093
1094
    /* get/set the 'replaced' flag */
1095
    int is_replaced() const { return replaced_; }
1096
    void set_replaced(int f) { replaced_ = f; }
1097
1098
    /* next anchor in same list */
1099
    CTcStreamAnchor *nxt_;
1100
1101
    /* 
1102
     *   Pointer to fixup list head for the object - this is a list of the
1103
     *   fixups for references to this object.  Because an object might
1104
     *   have a fixup list before the object is added to the data stream
1105
     *   (anything that is reachable through multiple references, such as
1106
     *   a function in a symbol table, could have such a fixup list),
1107
     *   we'll set this to refer to the external fixup list.  Otherwise,
1108
     *   we'll set it to point to our own internal fixup list head.  
1109
     */
1110
    struct CTcAbsFixup **fixup_list_head_;
1111
1112
    /* 
1113
     *   If we have an external fixup list, we need to store the symbol
1114
     *   table entry that owns the fixup list; otherwise, we must store
1115
     *   the fixup list head.  Since we only need one or the other,
1116
     *   compress the storage into a union. 
1117
     */
1118
    union
1119
    {
1120
        /* 
1121
         *   internal fixup list - this is used if no external fixup list
1122
         *   exists for the object 
1123
         */
1124
        struct CTcAbsFixup *internal_fixup_head_;
1125
1126
        /* owning symbol table entry, if the fixup list is external */
1127
        class CTcSymbol *fixup_owner_sym_;
1128
    } fixup_info_;
1129
1130
    /* offset of this object in the stream */
1131
    ulong ofs_;
1132
1133
    /* 
1134
     *   final absolute address - this won't be known until the layout of
1135
     *   the pool or data segment containing this object is calculated,
1136
     *   which happens during the link phase 
1137
     */
1138
    ulong addr_;
1139
1140
    /* 
1141
     *   Flag: this code block has been replaced by another one.  This is
1142
     *   set when the function or property that owns this code is replaced
1143
     *   by another implementation; when this is set, the code block
1144
     *   should not be written to the image file. 
1145
     */
1146
    unsigned int replaced_ : 1;
1147
};
1148
1149
#endif /* TCGEN_H */
1150