cfad47cfa3/t3compiler/tads3/tct3.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* $Header: d:/cvsroot/tads/tads3/TCT3.H,v 1.5 1999/07/11 00:46:55 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
  tct3.h - TADS 3 compiler - T3 Virtual Machine Code Generator
12
Function
13
  
14
Notes
15
  
16
Modified
17
  04/30/99 MJRoberts  - Creation
18
*/
19
20
#ifndef TCT3_H
21
#define TCT3_H
22
23
#include "t3std.h"
24
#include "tcprs.h"
25
#include "vmop.h"
26
#include "vmtype.h"
27
#include "tct3ty.h"
28
29
/* ------------------------------------------------------------------------ */
30
/*
31
 *   include the T3-specific CVmRuntimeSymbols class definition 
32
 */
33
#include "vmrunsym.h"
34
35
36
/* ------------------------------------------------------------------------ */
37
/* 
38
 *   include the T3-specific final parse node classes 
39
 */
40
#include "tct3drv.h"
41
42
43
/* ------------------------------------------------------------------------ */
44
/*
45
 *   Define some internal compiler datatypes - any type
46
 *   VM_FIRST_INVALID_TYPE or higher is not used by the VM and can thus be
47
 *   used for our own internal types. 
48
 */
49
const vm_datatype_t VM_VOCAB_LIST = VM_MAKE_INTERNAL_TYPE(0);
50
51
52
/* ------------------------------------------------------------------------ */
53
/*
54
 *   Data structure sizes.  These are the sizes of various data structures
55
 *   that we write to the image file; these values are global to the
56
 *   entire image file, and are constants of the current file format.  
57
 */
58
59
/* method header size */
60
#define TCT3_METHOD_HDR_SIZE   10
61
62
/* exception table entry size */
63
#define TCT3_EXC_ENTRY_SIZE    10
64
65
/* debugger line record entry size */
66
#define TCT3_LINE_ENTRY_SIZE   10
67
68
/* debug table header size */
69
#define TCT3_DBG_HDR_SIZE      0
70
71
/* debugger local symbol record size */
72
#define TCT3_DBG_LCLSYM_HDR_SIZE  6
73
74
/* debugger record format version */
75
#define TCT3_DBG_FMT_VSN       1
76
77
/* ------------------------------------------------------------------------ */
78
/*
79
 *   Object file header flags.
80
 */
81
#define TCT3_OBJHDR_DEBUG   0x0001
82
83
84
/* ------------------------------------------------------------------------ */
85
/*
86
 *   Object Stream prefix flags.  Each object we write to the object
87
 *   stream starts with a prefix byte that we use to store some extra flag
88
 *   information about the object.  
89
 */
90
91
/* object has been replaced - do not write to image file */
92
#define TCT3_OBJ_REPLACED  0x0001
93
94
/* object has been modified */
95
#define TCT3_OBJ_MODIFIED  0x0002
96
97
/* object is transient */
98
#define TCT3_OBJ_TRANSIENT 0x0004
99
100
101
/* ------------------------------------------------------------------------ */
102
/*
103
 *   T3 metaclass object stream header sizes 
104
 */
105
106
/* 
107
 *   internal header size - this is an extra header the compiler adds for
108
 *   each object in an object stream
109
 *   
110
 *   UINT2 compiler_flags
111
 */
112
const size_t TCT3_OBJ_INTERNHDR_SIZE = 2;
113
114
/* offset of the internal header from the start of the object data */
115
const size_t TCT3_OBJ_INTERNHDR_OFS = 0;
116
117
/* offset of internal header flags */
118
const size_t TCT3_OBJ_INTERNHDR_FLAGS_OFS = TCT3_OBJ_INTERNHDR_OFS;
119
120
/* 
121
 *   T3 generic metaclass header - this is the header on every metaclass
122
 *   in a T3 image file 'OBJS' block.
123
 *   
124
 *   UINT4 object_id
125
 *.  UINT2 metaclass_specific_byte_count
126
 */
127
const size_t TCT3_META_HEADER_SIZE = 6;
128
129
/* 
130
 *   large metaclass header size - this is the same as the standard
131
 *   header, but uses a 32-bit size field rather than the 16-bit size
132
 *   field 
133
 */
134
const size_t TCT3_LARGE_META_HEADER_SIZE = 8;
135
136
/* offset of generic metaclass header from the start of an object's data */
137
const size_t TCT3_META_HEADER_OFS =
138
    TCT3_OBJ_INTERNHDR_OFS + TCT3_OBJ_INTERNHDR_SIZE;
139
140
/* ------------------------------------------------------------------------ */
141
/*
142
 *   tads-object metaclass object stream header sizes
143
 */
144
145
/* 
146
 *   tads-object header - each object with metaclass tads-object defines
147
 *   this header
148
 *   
149
 *.  UINT2 superclass_count
150
 *.  UINT2 property_count
151
 *.  UINT2 object_flags 
152
 */
153
const size_t TCT3_TADSOBJ_HEADER_SIZE = 6;
154
155
/* offset of the tads-object header from the start of an object's data */
156
const size_t TCT3_TADSOBJ_HEADER_OFS =
157
    TCT3_META_HEADER_OFS + TCT3_META_HEADER_SIZE;
158
159
/* 
160
 *   offset to the superclass table, which immediately follows the
161
 *   tads-object header 
162
 */
163
const size_t TCT3_TADSOBJ_SC_OFS =
164
    TCT3_TADSOBJ_HEADER_OFS + TCT3_TADSOBJ_HEADER_SIZE;
165
166
/* 
167
 *   size of a property table entry
168
 *   
169
 *.  UINT2 property_ID
170
 *.  DATAHOLDER value 
171
 */
172
const size_t TCT3_TADSOBJ_PROP_SIZE = 2 + VMB_DATAHOLDER;
173
174
/*
175
 *   T3 object flags 
176
 */
177
178
/* class flag - object is a class, not an instance */
179
#define TCT3_OBJFLG_CLASS 0x0001
180
181
182
/* ------------------------------------------------------------------------ */
183
/*
184
 *   Metaclass List Entry.  This list keeps track of the metaclasses that
185
 *   the image file is dependent upon, and the dynamic link mapping
186
 *   between metaclass ID in the image file and the universally unique
187
 *   metaclass name.  
188
 */
189
struct tc_meta_entry
190
{
191
    /* next entry in the list */
192
    tc_meta_entry *nxt;
193
194
    /* 
195
     *   metaclass symbol object, if present - we get the property list
196
     *   from the metaclass symbol 
197
     */
198
    class CTcSymMetaclass *sym;
199
200
    /* external (universally unique) metaclass name */
201
    char nm[1];
202
};
203
204
/*
205
 *   Fixed System Metaclasses.  The compiler must generate code for these
206
 *   metaclasses directly, so it pre-loads the metaclass dependency table
207
 *   with these metaclasses at initialization.  Because these entries are
208
 *   always loaded into the table in the same order, they have fixed table
209
 *   indices that we can define as constants here.
210
 */
211
212
/* TADS Object Metaclass */
213
const int TCT3_METAID_TADSOBJ = 0;
214
215
/* list metaclass */
216
const int TCT3_METAID_LIST = 1;
217
218
/* dictionary metaclass */
219
const int TCT3_METAID_DICT = 2;
220
221
/* grammar production metaclass */
222
const int TCT3_METAID_GRAMPROD = 3;
223
224
/* vector metaclass */
225
const int TCT3_METAID_VECTOR = 4;
226
227
/* anonymous function pointer */
228
const int TCT3_METAID_ANONFN = 5;
229
230
/* intrinsic class modifiers */
231
const int TCT3_METAID_ICMOD = 6;
232
233
/*
234
 *   IMPORTANT!!!  When adding new entries to this list of pre-defined
235
 *   metaclasses, you must:
236
 *   
237
 *   - update the 'last' constant below
238
 *   
239
 *   - add the new entry to CTcGenTarg::load_image_file_meta_table()
240
 */
241
242
/* last metaclass ID - adjust when new entries are added */
243
const int TCT3_METAID_LAST = 6;
244
245
/* ------------------------------------------------------------------------ */
246
/*
247
 *   Function set dependency list entry 
248
 */
249
struct tc_fnset_entry
250
{
251
    /* next entry in the list */
252
    tc_fnset_entry *nxt;
253
254
    /* external (universally unique) function set name */
255
    char nm[1];
256
};
257
258
/* ------------------------------------------------------------------------ */
259
/*
260
 *   Exception Table builder.  This object keeps track of the entries in
261
 *   an exception table under construction, so that the exception table
262
 *   for a function can be written to the code stream after all of the
263
 *   code in the function has been generated.  
264
 */
265
class CTcT3ExcTable
266
{
267
public:
268
    CTcT3ExcTable();
269
    
270
    ~CTcT3ExcTable()
271
    {
272
        /* if we've allocated a table, delete it */
273
        if (table_ != 0)
274
            t3free(table_);
275
    }
276
277
    /* 
278
     *   Set the current function or method's start offset.  The code
279
     *   generator for the function body should set this to the code
280
     *   stream offset of the start of the method header; this allows us
281
     *   to calculate the offsets of protected code and 'catch' blocks.
282
     *   
283
     *   Important: this is the code stream offset (G_cs->get_ofs()), not
284
     *   the final code pool address.  We need only relative offsets, so
285
     *   the code stream offset suffices (and is available much earlier in
286
     *   the code generation process).  
287
     */
288
    void set_method_ofs(ulong ofs) { method_ofs_ = ofs; }
289
290
    /* 
291
     *   Add a 'catch' entry.  The offsets are all code stream offsets
292
     *   (G_cs->get_ofs() values). 
293
     */
294
    void add_catch(ulong protected_start_ofs, ulong protected_end_ofs,
295
                   ulong exc_obj_id, ulong catch_block_ofs);
296
297
    /* 
298
     *   write our exception table to the code stream - writes to the G_cs
299
     *   global code stream object 
300
     */
301
    void write_to_code_stream();
302
303
    /* get the number of entries */
304
    size_t get_entry_count() const { return exc_used_; }
305
306
    /* clear the exception table - remove all entries */
307
    void clear_table() { exc_used_ = 0; }
308
309
protected:
310
    /* the starting offset of this method header */
311
    ulong method_ofs_;
312
313
    /* exception table */
314
    struct CTcT3ExcEntry *table_;
315
316
    /* number of entries used/allocated in our table */
317
    size_t exc_used_;
318
    size_t exc_alloced_;
319
};
320
321
/*
322
 *   Exception table entry 
323
 */
324
struct CTcT3ExcEntry
325
{
326
    /* start/end offset (from start of method header) of protected code */
327
    ulong start_ofs;
328
    ulong end_ofs;
329
330
    /* object ID of exception class caught */
331
    ulong exc_obj_id;
332
333
    /* 'catch' block starting offset (from start of method header) */
334
    ulong catch_ofs;
335
};
336
337
/* ------------------------------------------------------------------------ */
338
/*
339
 *   Data Stream Page Layout Manager.  This works with a CTcDataStream
340
 *   object (such as the constant pool or the code pool) to divide the
341
 *   stream into pages for the image file.  
342
 */
343
class CTcStreamLayout
344
{
345
public:
346
    CTcStreamLayout()
347
    {
348
        /* we don't know anything about our layout yet */
349
        page_size_ = 0;
350
        page_cnt_ = 0;
351
    }
352
    
353
    /* 
354
     *   Calculate my layout, given the maximum object size.  This can be
355
     *   called once the entire stream has been generated, hence the size
356
     *   of the largest indivisible item in the stream is known.  This
357
     *   will apply all fixups throughout the stream.
358
     *   
359
     *   If this is the first stream for this layout, is_first is true.
360
     *   If we're adding more pages, is_first is false, and max_len is
361
     *   ignored (so the caller must ensure that the max_len provided on
362
     *   laying out the first stream for this page set is adequate for all
363
     *   streams added to this layout).  
364
     */
365
    void calc_layout(class CTcDataStream *ds, ulong max_len, int is_first);
366
367
    /*
368
     *   Write the stream(s) to an image file.  We'll write the pool
369
     *   definition block and the pool pages.  This cannot be called until
370
     *   after calc_layout() has been called for all streams, because we
371
     *   must apply all fixups throughout the entire image before we can
372
     *   write out anything.  
373
     */
374
    void write_to_image(class CTcDataStream **ds_array, size_t ds_cnt,
375
                        class CVmImageWriter *image_writer, int pool_id,
376
                        uchar xor_mask);
377
378
    /* page size */
379
    ulong page_size_;
380
381
    /* number of pages used */
382
    size_t page_cnt_;
383
};
384
385
386
/* ------------------------------------------------------------------------ */
387
/*
388
 *   Debug line list page.  We keep a linked list of these pages, and
389
 *   allocate new entries out of the last page.  We keep going until the
390
 *   last page is filled up, then allocate a new page.  
391
 */
392
const size_t TCT3_DEBUG_LINE_PAGE_SIZE = 1024;
393
const size_t TCT3_DEBUG_LINE_REC_SIZE = 5;
394
struct tct3_debug_line_page
395
{
396
    /* next page in list */
397
    tct3_debug_line_page *nxt;
398
399
    /* 
400
     *   Entries on this page (each entry is a debug line record offset in
401
     *   the code stream).  Each entry consists of one byte for the code
402
     *   stream identifier (TCGEN_xxx_STREAM) and four bytes for a
403
     *   portable UINT4 with the offset in the stream.  
404
     */
405
    uchar line_ofs[TCT3_DEBUG_LINE_PAGE_SIZE * TCT3_DEBUG_LINE_REC_SIZE];
406
};
407
408
409
/* ------------------------------------------------------------------------ */
410
/*
411
 *   T3-specific code generator helper class.  This class provides a set
412
 *   of static functions that are useful for T3 code generation.  
413
 */
414
class CTcGenTarg
415
{
416
public:
417
    /* initialize the code generator */
418
    CTcGenTarg();
419
420
    /* destroy the code generator object */
421
    ~CTcGenTarg();
422
423
    /*
424
     *   Set the run-time metaclass dependency table index for a given
425
     *   metaclass, identified by 'name' (a string of length 'len').  'idx'
426
     *   is the run-time metaclass dependency index.
427
     *   
428
     *   When we're operating as part of an interactive debugger, the image
429
     *   loader must call this for each entry in the metaclass dependency
430
     *   table loaded from the image file.  This allows us to fix up our
431
     *   internal notion of the metaclass indices so that we generate code
432
     *   compatible with the actual loaded image file.
433
     *   
434
     *   The protocol is as follows: call start_image_file_meta_table(), then
435
     *   call load_image_file_meta_table() on each entry in the table, then
436
     *   call end_image_file_meta_table().  
437
     */
438
    void start_image_file_meta_table();
439
    void load_image_file_meta_table(const char *nm, size_t len, int idx);
440
    void end_image_file_meta_table();
441
442
    /*
443
     *   Allocate a new global property ID. 
444
     */
445
    tctarg_prop_id_t new_prop_id() { return next_prop_++; }
446
447
    /*
448
     *   Allocate a new global object ID. 
449
     */
450
    tctarg_obj_id_t new_obj_id() { return next_obj_++; }
451
452
    /* 
453
     *   add a metaclass to the dependency table - returns the index of
454
     *   the metaclass in the table 
455
     */
456
    int add_meta(const char *meta_extern_name, size_t len,
457
                 class CTcSymMetaclass *sym);
458
    int add_meta(const char *nm, class CTcSymMetaclass *sym)
459
        { return add_meta(nm, strlen(nm), sym); }
460
    int add_meta(const char *nm)
461
        { return add_meta(nm, strlen(nm), 0); }
462
463
    /* 
464
     *   Find a metaclass entry, adding it if it's not already there.  If
465
     *   the metaclass is already defined, and it has an associated
466
     *   symbol, we will not change the associated symbol - this will let
467
     *   the caller detect that the metaclass has been previously defined
468
     *   for a different symbol, which is usually an error. 
469
     */
470
    int find_or_add_meta(const char *nm, size_t len,
471
                         class CTcSymMetaclass *sym);
472
473
    /* get a metaclass symbol by the metaclass's global identifier */
474
    class CTcSymMetaclass *find_meta_sym(const char *nm, size_t len);
475
476
    /* 
477
     *   Find the metaclass table entry for a given global identifier.  If
478
     *   update_vsn is true, we'll update the entry stored in the table to
479
     *   the given version number if the given name's version number is
480
     *   higher than the one in the table.  If we find an entry, we'll
481
     *   fill in *entry_idx with the entry's index.  
482
     */
483
    tc_meta_entry *find_meta_entry(const char *nm, size_t len,
484
                                   int update_vsn, int *entry_idx);
485
486
    /* get/set the symbol for a given metaclass */
487
    class CTcSymMetaclass *get_meta_sym(int meta_idx);
488
    void set_meta_sym(int meta_idx, class CTcSymMetaclass *sym);
489
490
    /* get the number of metaclasses */
491
    int get_meta_cnt() const { return meta_cnt_; }
492
493
    /* get the external (universally unique) name for the given metaclass */
494
    const char *get_meta_name(int idx) const;
495
496
    /*
497
     *   Get the dependency table index for the given pre-defined metaclass,
498
     *   specified by the TCT3_METAID_xxx value. 
499
     */
500
    int get_predef_meta_idx(int id) const { return predef_meta_idx_[id]; }
501
502
    /*
503
     *   Add a function set to the dependency table - returns the index of
504
     *   the function set in the table 
505
     */
506
    int add_fnset(const char *fnset_extern_name, size_t len);
507
    int add_fnset(const char *fnset_extern_name)
508
        { return add_fnset(fnset_extern_name, strlen(fnset_extern_name)); }
509
510
    /* get the name of a function set given its index */
511
    const char *get_fnset_name(int idx) const;
512
513
    /* get the number of defined function sets */
514
    int get_fnset_cnt() const { return fnset_cnt_; }
515
516
    /*
517
     *   Notify the code generator that parsing is finished.  This should
518
     *   be called after parsing and before code generation begins.  
519
     */
520
    void parsing_done();
521
522
    /*
523
     *   Note a string value's length.  This should be invoked during the
524
     *   parsing phase for each constant string value.  We'll keep track
525
     *   of the largest constant data in the file, so that after parsing
526
     *   is finished, we'll know the minimum size we need for each
527
     *   constant pool page.  This doesn't actually allocate any space in
528
     *   the constant pool; this merely keeps track of the longest string
529
     *   we'll eventually need to store.  
530
     */
531
    void note_str(size_t len);
532
533
    /*
534
     *   Note number of elements in a constant list value.  This is the
535
     *   list equivalent of note_str().  
536
     */
537
    void note_list(size_t element_count);
538
539
    /*
540
     *   Note the length of a code block's byte code.  This should be
541
     *   invoked during code generation for each code block; we'll keep
542
     *   track of the longest byte code block, so that after code
543
     *   generation is complete, we'll know the minimum size we need for
544
     *   each code pool page.  
545
     */
546
    void note_bytecode(ulong len);
547
548
    /*
549
     *   Notify the code generator that we're replacing an object (via the
550
     *   "replace" statement) at the given stream offset.  We'll mark the
551
     *   data in the stream as deleted so that we don't write it to the
552
     *   image file.  
553
     */
554
    void notify_replace_object(ulong stream_ofs);
555
556
    /*
557
     *   Write to an object file.  The compiler calls this after all
558
     *   parsing and code generation are completed to write an object
559
     *   file, which can then be linked with other object files to create
560
     *   an image file.
561
     */
562
    void write_to_object_file(class CVmFile *object_fp,
563
                              class CTcMake *make_obj);
564
565
    /*
566
     *   Load an object file.  Returns zero on success, non-zero on error.
567
     */
568
    int load_object_file(CVmFile *fp, const textchar_t *fname);
569
570
    /*
571
     *   Write the image file.  The compiler calls this after all parsing
572
     *   and code generation are completed to write an image file.  We
573
     *   must apply all fixups, assign the code and constant pool layouts,
574
     *   and write the data to the image file.  
575
     */
576
    void write_to_image(class CVmFile *image_fp, uchar data_xor_mask,
577
                        const char tool_data[4]);
578
579
    /* generate synthesized code during linking */
580
    void build_synthesized_code();
581
582
    /* generate code for a dictionary object */
583
    void gen_code_for_dict(class CTcDictEntry *dict);
584
585
    /* generate code for a grammar production object */
586
    void gen_code_for_gramprod(class CTcGramProdEntry *prod);
587
588
    /* get the maximum string/list/bytecode lengths */
589
    size_t get_max_str_len() const { return max_str_len_; }
590
    size_t get_max_list_cnt() const { return max_list_cnt_; }
591
    size_t get_max_bytecode_len() const { return max_bytecode_len_; }
592
593
    /*
594
     *   Add a debug line record.  If we're in debug mode, this will clear
595
     *   the peephole optimizer to ensure that the line record doesn't get
596
     *   confused due to compression of opcodes.  
597
     */
598
    void add_line_rec(class CTcTokFileDesc *file, long linenum);
599
600
    /* write an opcode to the output stream */
601
    void write_op(uchar opc);
602
603
    /* write a CALLPROP instruction */
604
    void write_callprop(int argc, int varargs, vm_prop_id_t prop);
605
606
    /* 
607
     *   Determine if we can skip an opcode for peephole optimization.
608
     *   We'll look at the previous opcode to determine if this opcode is
609
     *   reachable, and we'll indicate that we should suppress the new
610
     *   opcode if not.  
611
     */
612
    int can_skip_op();
613
614
    /* 
615
     *   Add a string to the constant pool, and create a fixup for the
616
     *   item for a reference from the given stream at the given offset.  
617
     */
618
    void add_const_str(const char *str, size_t len,
619
                       class CTcDataStream *ds, ulong ofs);
620
621
    /* 
622
     *   Add a list to the constant pool, and create a fixup for the item
623
     *   for a reference from the given stream at the given offset.  
624
     */
625
    void add_const_list(class CTPNList *lst,
626
                        class CTcDataStream *ds, ulong ofs);
627
628
    /*
629
     *   Write a constant value (in the compiler's internal
630
     *   representation, a CTcConstVal structure) to a given buffer in T3
631
     *   image file DATA_HOLDER format.  Write at a given offset, or at
632
     *   the current write offset.  
633
     */
634
    void write_const_as_dh(class CTcDataStream *ds, ulong ofs,
635
                           const class CTcConstVal *src);
636
    void write_const_as_dh(class CTcDataStream *ds,
637
                           const class CTcConstVal *src);
638
639
    /*
640
     *   Clear the peephole optimizer state.  This must be invoked
641
     *   whenever a jump label is defined.  We can't combine an
642
     *   instruction at a jump destination with anything previous: the
643
     *   instruction at a jump destination must be generated as-is, rather
644
     *   than being combined with the preceding instruction, since someone
645
     *   could jump directly to it.  
646
     */
647
    void clear_peephole()
648
    {
649
        last_op_ = OPC_NOP;
650
        second_last_op_ = OPC_NOP;
651
    }
652
653
    /* get the last opcode we generated */
654
    uchar get_last_op() const { return last_op_; }
655
656
    /*
657
     *   Remove the last JMP instruction.  This is used when we detect
658
     *   that we just generated a JMP ahead to the very next instruction,
659
     *   in which case we can eliminate the JMP, since it has no effect. 
660
     */
661
    void remove_last_jmp();
662
663
    /*
664
     *   Stack depth counting.  While we're generating code for a code
665
     *   block (a function or method), we'll keep track of our stack push
666
     *   and pop operations, so that we can monitor the maximum stack
667
     *   depth.  In order for the stack depth to be calculable at compile
668
     *   time, the code generator must take care that each individual
669
     *   statement is stack-neutral (i.e, the stack comes out of each
670
     *   statement at the same depth as when it entered the statement), so
671
     *   that jumps, iterations, and other variables we can't analyze
672
     *   statically can be ignored.
673
     */
674
675
    /* 
676
     *   reset the stack depth counters - call this at the start
677
     *   generating of each code block 
678
     */
679
    void reset_sp_depth() { sp_depth_ = max_sp_depth_ = 0; }
680
681
    /* 
682
     *   get the maximum stack depth for the current function - use this
683
     *   when finished generating a code block to determine the maximum
684
     *   stack space needed by the code block 
685
     */
686
    int get_max_sp_depth() const { return max_sp_depth_; }
687
688
    /* get the current stack depth */
689
    int get_sp_depth() const { return sp_depth_; }
690
691
    /* record a push - increments the current stack depth */
692
    void note_push() { note_push(1); }
693
694
    /* record a push of a given number of stack elements */
695
    void note_push(int cnt)
696
    {
697
        sp_depth_ += cnt;
698
        if (sp_depth_ > max_sp_depth_)
699
            max_sp_depth_ = sp_depth_;
700
    }
701
702
    /* record a pop - decrements the current stack depth */
703
    void note_pop() { note_pop(1); }
704
705
    /* record a pop of a given number of stack elements */
706
    void note_pop(int cnt) { sp_depth_ -= cnt; }
707
708
    /* record a full stack reset back to function entry conditions */
709
    void note_rst() { sp_depth_ = 0; }
710
711
    /*
712
     *   Open/close a method/function.  "Open" generates a placeholder method
713
     *   header and sets up our generator globals to prepare for a new
714
     *   method.  "Close" goes back and fills in the final method header
715
     *   based on the code generated since "Open".
716
     */
717
    void open_method(class CTcCodeStream *stream,
718
                     class CTcSymbol *fixup_owner_sym,
719
                     struct CTcAbsFixup **fixup_list_head,
720
                     class CTPNCodeBody *code_body,
721
                     class CTcPrsSymtab *goto_tab,
722
                     int argc, int varargs,
723
                     int is_constructor, int is_self_available,
724
                     struct tct3_method_gen_ctx *ctx);
725
    void close_method(int local_cnt,
726
                      class CTcTokFileDesc *end_desc, long end_linenum,
727
                      struct tct3_method_gen_ctx *ctx);
728
    void close_method_cleanup(struct tct3_method_gen_ctx *ctx);
729
730
    /*
731
     *   Generate a TadsObject header to a data stream 
732
     */
733
    void open_tadsobj(struct tct3_tadsobj_ctx *ctx,
734
                      CTcDataStream *stream,
735
                      vm_obj_id_t obj_id, int sc_cnt, int prop_cnt,
736
                      unsigned int internal_flags, unsigned int vm_flags);
737
    void close_tadsobj(struct tct3_tadsobj_ctx *ctx);
738
739
    /*
740
     *   Linker support: ensure that the given intrinsic class has a modifier
741
     *   object.  If there's no modifier, we'll create one and add code for
742
     *   it to the intrinsic class modifier stream. 
743
     */
744
    void linker_ensure_mod_obj(CTcSymMetaclass *mc_sym);
745
    void linker_ensure_mod_obj(const char *name, size_t len);
746
747
    /* 
748
     *   get my exception table object - this is used to construct a
749
     *   method's exception table during code generation, and to write the
750
     *   table to the code stream 
751
     */
752
    CTcT3ExcTable *get_exc_table() { return &exc_table_; }
753
754
    /* determine if we're compiling a constructor */
755
    int is_in_constructor() const { return in_constructor_; }
756
    void set_in_constructor(int f) { in_constructor_ = f; }
757
758
    /*   
759
     *   set the method offset - the code body object calls this when it's
760
     *   about to start generating code to let us know the offset of the
761
     *   current method 
762
     */
763
    void set_method_ofs(ulong ofs);
764
765
    /*
766
     *   Add a debug line table to our list.  We keep track of all of the
767
     *   debug line record tables in the program, so that we can store the
768
     *   list in the object file.  We need this information in the object
769
     *   file because each debug line record table in an object file must
770
     *   be fixed up at link time after loading the object file. 
771
     */
772
    void add_debug_line_table(ulong ofs);
773
774
    /*
775
     *   Set debug evaluation mode.  If 'speculative' is true, it means
776
     *   that we're generating an expression for speculative evaluation,
777
     *   in which case the evaluation must fail if it would have any side
778
     *   effects (such as calling a method, displaying a string, or
779
     *   assigning a value).  'stack_level' is the enclosing stack level
780
     *   at which to evaluate the expression; 0 is the last active
781
     *   non-debug stack level, 1 is the first enclosing level, and so on.
782
     */
783
    void set_debug_eval(int speculative, int level)
784
    {
785
        /* note that we're evaluating for the debugger */
786
        eval_for_debug_ = TRUE;
787
788
        /* note the speculative mode */
789
        speculative_ = speculative;
790
791
        /* note the stack level */
792
        debug_stack_level_ = level;
793
    }
794
795
    /* set normal evaluation mode */
796
    void set_normal_eval() { eval_for_debug_ = FALSE; }
797
798
    /* determine if we're in debugger evaluation mode */
799
    int is_eval_for_debug() const { return eval_for_debug_; }
800
801
    /* determine if we're in speculative evaluation mode */
802
    int is_speculative() const { return eval_for_debug_ && speculative_; }
803
804
    /* get the active debugger stack level */
805
    int get_debug_stack_level() const { return debug_stack_level_; }
806
807
    /* 
808
     *   Generate a BigNumber object, returning the object ID.  The input
809
     *   text gives the source representation of the number. 
810
     */
811
    vm_obj_id_t gen_bignum_obj(const char *txt, size_t len);
812
813
private:
814
    /* eliminate jump-to-jump sequences */
815
    void remove_jumps_to_jumps(class CTcCodeStream *str, ulong start_ofs);
816
817
    /*
818
     *   Calculate pool layouts.  This is called after all code generation
819
     *   is completed; at this point, the T3 code generator can determine
820
     *   how the code pages will be laid out, since we now know the size
821
     *   of the largest single chunk of code.
822
     *   
823
     *   We'll fill in *first_static_page with the page number in the code
824
     *   pool of the first page of code containing static initializers.
825
     *   We group all of the static initializer code together at the end
826
     *   of the code pool to allow the pre-initialization re-writer to
827
     *   omit all of the static code pages from the final image file.  
828
     */
829
    void calc_pool_layouts(size_t *first_static_page);
830
831
    /* 
832
     *   Write a TADS object stream to the image file.  This routine will
833
     *   fix up the property table in each object to put the table in
834
     *   sorted order.  
835
     */
836
    void write_tads_objects_to_image(class CTcDataStream *obj_stream,
837
                                     class CVmImageWriter *image_writer,
838
                                     int metaclass_idx);
839
840
    /* 
841
     *   write the TADS objects of one particular type - transient or
842
     *   persistent - to the image file 
843
     */
844
    void write_tads_objects_to_image(CTcDataStream *os,
845
        CVmImageWriter *image_writer, int meta_idx, int trans);
846
847
    /* 
848
     *   Write an object stream of non-TADS objects to the image file.
849
     *   This writes the objects as-is, without looking into their
850
     *   contents at all.  
851
     */
852
    void write_nontads_objs_to_image(class CTcDataStream *obj_stream,
853
                                     class CVmImageWriter *image_writer,
854
                                     int metaclass_idx, int large_obs);
855
856
    /* 
857
     *   Sort an object's property table, and compress the table to remove
858
     *   deleted properties.  Returns the final size of the object data to
859
     *   write to the image file, which could differ from the original
860
     *   size, because we might remove property slots from the property
861
     *   data.  If we do change the size of the property table, we'll
862
     *   update the stream data to reflect the new property count and
863
     *   metaclass data size.  
864
     */
865
    size_t sort_object_prop_table(class CTcDataStream *obj_stream,
866
                                  ulong start_ofs);
867
868
    /* write the function-set dependency table to an object file */
869
    void write_funcdep_to_object_file(class CVmFile *fp);
870
871
    /* write the metaclass dependency table to an object file */
872
    void write_metadep_to_object_file(class CVmFile *fp);
873
874
    /* load the function set dependency table from an object file */
875
    void load_funcdep_from_object_file(class CVmFile *fp,
876
                                       const textchar_t *fname);
877
878
    /* load the metaclass dependency table from an object file */
879
    void load_metadep_from_object_file(class CVmFile *fp,
880
                                       const textchar_t *fname);
881
882
    /* look up a required or optional property by name */
883
    vm_prop_id_t look_up_prop(const char *propname, int required,
884
                              int err_if_undef, int err_if_not_prop);
885
886
    /* build the IntrinsicClass instances */
887
    void build_intrinsic_class_objs(CTcDataStream *str);
888
889
    /* build the source file line maps */
890
    void build_source_line_maps();
891
892
    /* build the multi-method initializer list */
893
    void build_multimethod_initializers();
894
895
    /* symbol table enumerator callback for the multi-method initializers */
896
    static void multimethod_init_cb(void *ctx, CTcSymbol *sym);
897
898
    /* symbol table enumerator callback for the multi-method stubs */
899
    static void multimethod_stub_cb(void *ctx, CTcSymbol *sym);
900
901
    /* write the static initializer list to the image file */
902
    void write_static_init_list(CVmImageWriter *image_writer,
903
                                ulong main_cs_size);
904
905
    /* write the list of source file descriptors to an image file */
906
    void write_sources_to_image(class CVmImageWriter *image_writer);
907
908
    /* write the global symbol table to an object file */
909
    void write_global_symbols_to_image(class CVmImageWriter *image_writer);
910
911
    /* write the method header list to the image file */
912
    void write_method_list_to_image(class CVmImageWriter *image_writer);
913
914
    /* write macro definitions to the image file */
915
    void write_macros_to_image(class CVmImageWriter *image_writer);
916
917
    /* write the list of source file descriptors to an object file */
918
    void write_sources_to_object_file(class CVmFile *fp);
919
920
    /* 
921
     *   read the list of sources from an object file, adding the sources
922
     *   to the tokenizer's internal list 
923
     */
924
    void read_sources_from_object_file(class CVmFile *fp);
925
926
    /* load debug records from an object file */
927
    void load_debug_records_from_object_file(class CVmFile *fp,
928
                                             const textchar_t *fname,
929
                                             ulong main_cs_start_ofs,
930
                                             ulong static_cs_start_ofs);
931
932
    /* fix up a debug line record table for the object file */
933
    void fix_up_debug_line_table(class CTcCodeStream *cs,
934
                                 ulong line_table_ofs, int first_filedesc);
935
936
    /* hash table enumerator callback - generate dictionary code */
937
    static void enum_dict_gen_cb(void *ctx, class CVmHashEntry *entry);
938
        
939
940
    /* most recent opcodes we've written, for peephole optimization */
941
    uchar last_op_;
942
    uchar second_last_op_;
943
944
    /* maximum constant string length seen during parsing */
945
    size_t max_str_len_;
946
947
    /* maximum list element count seen during parsing */
948
    size_t max_list_cnt_;
949
950
    /* maximum byte code block generated during code generation */
951
    size_t max_bytecode_len_;
952
953
    /* head and tail of metaclass list */
954
    tc_meta_entry *meta_head_;
955
    tc_meta_entry *meta_tail_;
956
957
    /* number of entries in metaclass list so far */
958
    int meta_cnt_;
959
960
    /* head and tail of function set list */
961
    tc_fnset_entry *fnset_head_;
962
    tc_fnset_entry *fnset_tail_;
963
964
    /* number of function sets in the list */
965
    int fnset_cnt_;
966
967
    /* next available global property ID */
968
    vm_prop_id_t next_prop_;
969
970
    /* next available global object ID */
971
    vm_obj_id_t next_obj_;
972
973
    /* current stack depth */
974
    int sp_depth_;
975
976
    /* maximum stack depth in current code block */
977
    int max_sp_depth_;
978
979
    /* exception table for current code block */
980
    CTcT3ExcTable exc_table_;
981
982
    /* constant pool layout manager */
983
    CTcStreamLayout const_layout_;
984
985
    /* code pool layout manager */
986
    CTcStreamLayout code_layout_;
987
988
    /* first/last page of debug line list */
989
    tct3_debug_line_page *debug_line_head_;
990
    tct3_debug_line_page *debug_line_tail_;
991
992
    /* total number of debug line list entries used so far */
993
    ulong debug_line_cnt_;
994
995
    /*
996
     *   Object ID of the multi-method static initializer object.  This will
997
     *   be set by build_multimethod_initializers() if we end up creating any
998
     *   registration code.  
999
     */
1000
    vm_obj_id_t mminit_obj_;
1001
1002
    /* 
1003
     *   property sorting buffer - this is space we allocate to copy an
1004
     *   object's property table for sorting 
1005
     */
1006
    char *sort_buf_;
1007
    size_t sort_buf_size_;
1008
1009
    /* flag: we're currently compiling a constructor */
1010
    uint in_constructor_ : 1;
1011
1012
    /* flag: we're generating an expression for debugger use */
1013
    uint eval_for_debug_ : 1;
1014
1015
    /* flag: we're generating a debugger speculative evaluation expression */
1016
    uint speculative_ : 1;
1017
1018
    /* 
1019
     *   debugger active stack context level - valid when eval_for_debug_
1020
     *   is true 
1021
     */
1022
    int debug_stack_level_;
1023
1024
    /*
1025
     *   Static table of image file metaclass dependency indices for the
1026
     *   pre-defined metaclasses (i.e., the metaclasses that the compiler
1027
     *   specifically generates code for).  When we compile a program, these
1028
     *   are determined by the compiler simply according to the order in
1029
     *   which it builds its own initial table of the known metaclasses.
1030
     *   When we're debugging, we need to get these values from the image
1031
     *   file.
1032
     *   
1033
     *   This is a simple translation table - we translate from our internal
1034
     *   index (a TCT3_METAID_xxx value) to the corresponding dependency
1035
     *   index in the actual image file.  So, we just need as many of these
1036
     *   as there TCT3_METAID_xxx indices.  We never need these for any
1037
     *   additional metaclasses that might exist - we only care about the
1038
     *   ones that the compiler specifically knows about in advance.  
1039
     */
1040
    int predef_meta_idx_[TCT3_METAID_LAST + 1];
1041
};
1042
1043
/* ------------------------------------------------------------------------ */
1044
/*
1045
 *   Method generator context 
1046
 */
1047
struct tct3_method_gen_ctx
1048
{
1049
    /* output code stream */
1050
    class CTcCodeStream *stream;
1051
1052
    /* stream anchor */
1053
    struct CTcStreamAnchor *anchor;
1054
1055
    /* method header offset in stream */
1056
    ulong method_ofs;
1057
1058
    /* starting and ending code offset in stream */
1059
    ulong code_start_ofs;
1060
    ulong code_end_ofs;
1061
1062
    /* enclosing code body */
1063
    class CTPNCodeBody *old_code_body;
1064
};
1065
1066
/* ------------------------------------------------------------------------ */
1067
/*
1068
 *   TadsObject header writer context 
1069
 */
1070
struct tct3_tadsobj_ctx
1071
{
1072
    /* start of object header in data stream */
1073
    ulong obj_ofs;
1074
1075
    /* data stream to which we're writing the object */
1076
    CTcDataStream *stream;
1077
};
1078
1079
1080
#endif /* TCT3_H */
1081