]> git.sur5r.net Git - cc65/blob - src/ca65/symentry.c
Maintain some additional information for scopes. Write a dummy scope section
[cc65] / src / ca65 / symentry.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                symentry.c                                 */
4 /*                                                                           */
5 /*              Symbol table entry for the ca65 macroassembler               */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2008 Ullrich von Bassewitz                                       */
10 /*               Roemerstrasse 52                                            */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <string.h>
37
38 /* common */
39 #include "addrsize.h"
40 #include "xmalloc.h"
41
42 /* ca65 */
43 #include "error.h"
44 #include "expr.h"
45 #include "global.h"
46 #include "scanner.h"
47 #include "segment.h"
48 #include "spool.h"
49 #include "studyexpr.h"          /* ### */
50 #include "symentry.h"
51 #include "symtab.h"
52
53
54
55 /*****************************************************************************/
56 /*                                   Data                                    */
57 /*****************************************************************************/
58
59
60
61 /* List of all symbol table entries */
62 SymEntry* SymList = 0;
63
64 /* Pointer to last defined symbol */
65 SymEntry* SymLast = 0;
66
67
68
69 /*****************************************************************************/
70 /*                                   Code                                    */
71 /*****************************************************************************/
72
73
74
75 SymEntry* NewSymEntry (const StrBuf* Name, unsigned Flags)
76 /* Allocate a symbol table entry, initialize and return it */
77 {
78     unsigned I;
79
80     /* Allocate memory */
81     SymEntry* S = xmalloc (sizeof (SymEntry));
82
83     /* Initialize the entry */
84     S->Left       = 0;
85     S->Right      = 0;
86     S->Locals     = 0;
87     S->Sym.Tab    = 0;
88     S->Pos        = CurPos;
89     for (I = 0; I < sizeof (S->GuessedUse) / sizeof (S->GuessedUse[0]); ++I) {
90         S->GuessedUse[I] = 0;
91     }
92     S->Flags      = Flags;
93     S->Expr       = 0;
94     S->ExprRefs   = AUTO_COLLECTION_INITIALIZER;
95     S->ExportSize = ADDR_SIZE_DEFAULT;
96     S->AddrSize   = ADDR_SIZE_DEFAULT;
97     memset (S->ConDesPrio, 0, sizeof (S->ConDesPrio));
98     S->Name       = GetStrBufId (Name);
99
100     /* Insert it into the list of all entries */
101     S->List = SymList;
102     SymList = S;
103
104     /* Return the initialized entry */
105     return S;
106 }
107
108
109
110 int SymSearchTree (SymEntry* T, const StrBuf* Name, SymEntry** E)
111 /* Search in the given tree for a name. If we find the symbol, the function
112  * will return 0 and put the entry pointer into E. If we did not find the
113  * symbol, and the tree is empty, E is set to NULL. If the tree is not empty,
114  * E will be set to the last entry, and the result of the function is <0 if
115  * the entry should be inserted on the left side, and >0 if it should get
116  * inserted on the right side.
117  */
118 {
119     /* Is there a tree? */
120     if (T == 0) {
121         *E = 0;
122         return 1;
123     }
124
125     /* We have a table, search it */
126     while (1) {
127
128         /* Get the symbol name */
129         const StrBuf* SymName = GetStrBuf (T->Name);
130
131         /* Choose next entry */
132         int Cmp = SB_Compare (Name, SymName);
133         if (Cmp < 0 && T->Left) {
134             T = T->Left;
135         } else if (Cmp > 0&& T->Right) {
136             T = T->Right;
137         } else {
138             /* Found or end of search, return the result */
139             *E = T;
140             return Cmp;
141         }
142     }
143 }
144
145
146
147 void SymRef (SymEntry* S)
148 /* Mark the given symbol as referenced */
149 {
150     /* Mark the symbol as referenced */
151     S->Flags |= SF_REFERENCED;
152 }
153
154
155
156 void SymTransferExprRefs (SymEntry* From, SymEntry* To)
157 /* Transfer all expression references from one symbol to another. */
158 {
159     unsigned I;
160
161     for (I = 0; I < CollCount (&From->ExprRefs); ++I) {
162
163         /* Get the expression node */
164         ExprNode* E = CollAtUnchecked (&From->ExprRefs, I);
165
166         /* Safety */
167         CHECK (E->Op == EXPR_SYMBOL && E->V.Sym == From);
168
169         /* Replace the symbol reference */
170         E->V.Sym = To;
171
172         /* Add the expression reference */
173         SymAddExprRef (To, E);
174     }
175
176     /* Remove all symbol references from the old symbol */
177     CollDeleteAll (&From->ExprRefs);
178 }
179
180
181
182 static void SymReplaceExprRefs (SymEntry* S)
183 /* Replace the references to this symbol by a copy of the symbol expression */
184 {
185     unsigned I;
186     long     Val;
187
188     /* Check if the expression is const and get its value */
189     int IsConst = IsConstExpr (S->Expr, &Val);
190     CHECK (IsConst);
191
192     /* Loop over all references */
193     for (I = 0; I < CollCount (&S->ExprRefs); ++I) {
194
195         /* Get the expression node */
196         ExprNode* E = CollAtUnchecked (&S->ExprRefs, I);
197
198         /* Safety */
199         CHECK (E->Op == EXPR_SYMBOL && E->V.Sym == S);
200
201         /* We cannot touch the root node, since there are pointers to it.
202          * Replace it by a literal node.
203          */
204         E->Op = EXPR_LITERAL;
205         E->V.IVal = Val;
206     }
207
208     /* Remove all symbol references from the symbol */
209     CollDeleteAll (&S->ExprRefs);
210 }
211
212
213
214 void SymDef (SymEntry* S, ExprNode* Expr, unsigned char AddrSize, unsigned Flags)
215 /* Define a new symbol */
216 {
217     if (S->Flags & SF_IMPORT) {
218         /* Defined symbol is marked as imported external symbol */
219         Error ("Symbol `%m%p' is already an import", GetSymName (S));
220         return;
221     }
222     if ((Flags & SF_VAR) != 0 && (S->Flags & (SF_EXPORT | SF_GLOBAL))) {
223         /* Variable symbols cannot be exports or globals */
224         Error ("Var symbol `%m%p' cannot be an export or global symbol", GetSymName (S));
225         return;
226     }
227     if (S->Flags & SF_DEFINED) {
228         /* Multiple definition. In case of a variable, this is legal. */
229         if ((S->Flags & SF_VAR) == 0) {
230             Error ("Symbol `%m%p' is already defined", GetSymName (S));
231             S->Flags |= SF_MULTDEF;
232             return;
233         } else {
234             /* Redefinition must also be a variable symbol */
235             if ((Flags & SF_VAR) == 0) {
236                 Error ("Symbol `%m%p' is already different kind", GetSymName (S));
237                 return;
238             }
239             /* Delete the current symbol expression, since it will get
240              * replaced
241              */
242             FreeExpr (S->Expr);
243             S->Expr = 0;
244         }
245     }
246
247     /* Map a default address size to a real value */
248     if (AddrSize == ADDR_SIZE_DEFAULT) {
249         /* ### Must go! Delay address size calculation until end of assembly! */
250         ExprDesc ED;
251         ED_Init (&ED);
252         StudyExpr (Expr, &ED);
253         AddrSize = ED.AddrSize;
254         ED_Done (&ED);
255     }
256
257     /* Set the symbol value */
258     S->Expr = Expr;
259
260     /* In case of a variable symbol, walk over all expressions containing
261      * this symbol and replace the (sub-)expression by the literal value of
262      * the tree. Be sure to replace the expression node in place, since there
263      * may be pointers to it.
264      */
265     if (Flags & SF_VAR) {
266         SymReplaceExprRefs (S);
267     }
268
269     /* If the symbol is marked as global, export it. Address size is checked
270      * below.
271      */
272     if (S->Flags & SF_GLOBAL) {
273         S->Flags = (S->Flags & ~SF_GLOBAL) | SF_EXPORT;
274     }
275
276     /* Mark the symbol as defined and use the given address size */
277     S->Flags |= (SF_DEFINED | Flags);
278     S->AddrSize = AddrSize;
279
280     /* If the symbol is exported, check the address sizes */
281     if (S->Flags & SF_EXPORT) {
282         if (S->ExportSize == ADDR_SIZE_DEFAULT) {
283             /* Use the real size of the symbol */
284             S->ExportSize = S->AddrSize;
285         } else if (S->AddrSize > S->ExportSize) {
286             /* We're exporting a symbol smaller than it actually is */
287             PWarning (GetSymPos (S), 1, "Symbol `%m%p' is %s but exported %s",
288                       GetSymName (S), AddrSizeToStr (S->AddrSize),
289                       AddrSizeToStr (S->ExportSize));
290         }
291     }
292
293     /* If this is not a local symbol, remember it as the last global one */
294     if ((S->Flags & SF_LOCAL) == 0) {
295         SymLast = S;
296     }
297 }
298
299
300
301 void SymImport (SymEntry* S, unsigned char AddrSize, unsigned Flags)
302 /* Mark the given symbol as an imported symbol */
303 {
304     if (S->Flags & SF_DEFINED) {
305         Error ("Symbol `%m%p' is already defined", GetSymName (S));
306         S->Flags |= SF_MULTDEF;
307         return;
308     }
309     if (S->Flags & SF_EXPORT) {
310         /* The symbol is already marked as exported symbol */
311         Error ("Cannot import exported symbol `%m%p'", GetSymName (S));
312         return;
313     }
314
315     /* If no address size is given, use the address size of the enclosing
316      * segment.
317      */
318     if (AddrSize == ADDR_SIZE_DEFAULT) {
319         AddrSize = GetCurrentSegAddrSize ();
320     }
321
322     /* If the symbol is marked as import or global, check the address size,
323      * then do silently remove the global flag.
324      */
325     if (S->Flags & SF_IMPORT) {
326         if ((Flags & SF_FORCED) != (S->Flags & SF_FORCED)) {
327             Error ("Redeclaration mismatch for symbol `%m%p'", GetSymName (S));
328         }
329         if (AddrSize != S->AddrSize) {
330             Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
331         }
332     }
333     if (S->Flags & SF_GLOBAL) {
334         S->Flags &= ~SF_GLOBAL;
335         if (AddrSize != S->AddrSize) {
336             Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
337         }
338     }
339
340     /* Set the symbol data */
341     S->Flags |= (SF_IMPORT | Flags);
342     S->AddrSize = AddrSize;
343 }
344
345
346
347 void SymExport (SymEntry* S, unsigned char AddrSize, unsigned Flags)
348 /* Mark the given symbol as an exported symbol */
349 {
350     /* Check if it's ok to export the symbol */
351     if (S->Flags & SF_IMPORT) {
352         /* The symbol is already marked as imported external symbol */
353         Error ("Symbol `%m%p' is already an import", GetSymName (S));
354         return;
355     }
356     if (S->Flags & SF_VAR) {
357         /* Variable symbols cannot be exported */
358         Error ("Var symbol `%m%p' cannot be exported", GetSymName (S));
359         return;
360     }
361
362     /* If the symbol was marked as global before, remove the global flag and
363      * proceed, but check the address size.
364      */
365     if (S->Flags & SF_GLOBAL) {
366         if (AddrSize != S->ExportSize) {
367             Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
368         }
369         S->Flags &= ~SF_GLOBAL;
370     }
371
372     /* If the symbol was already marked as an export, but wasn't defined
373      * before, the address sizes in both definitions must match.
374      */
375     if ((S->Flags & (SF_EXPORT|SF_DEFINED)) == SF_EXPORT) {
376         if (S->ExportSize != AddrSize) {
377             Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
378         }
379     }
380     S->ExportSize = AddrSize;
381
382     /* If the symbol is already defined, check symbol size against the
383      * exported size.
384      */
385     if (S->Flags & SF_DEFINED) {
386         if (S->ExportSize == ADDR_SIZE_DEFAULT) {
387             /* No export size given, use the real size of the symbol */
388             S->ExportSize = S->AddrSize;
389         } else if (S->AddrSize > S->ExportSize) {
390             /* We're exporting a symbol smaller than it actually is */
391             Warning (1, "Symbol `%m%p' is %s but exported %s",
392                      GetSymName (S), AddrSizeToStr (S->AddrSize),
393                      AddrSizeToStr (S->ExportSize));
394         }
395     }
396
397     /* Set the symbol data */
398     S->Flags |= (SF_EXPORT | SF_REFERENCED | Flags);
399 }
400
401
402
403 void SymGlobal (SymEntry* S, unsigned char AddrSize, unsigned Flags)
404 /* Mark the given symbol as a global symbol, that is, as a symbol that is
405  * either imported or exported.
406  */
407 {
408     if (S->Flags & SF_VAR) {
409         /* Variable symbols cannot be exported or imported */
410         Error ("Var symbol `%m%p' cannot be made global", GetSymName (S));
411         return;
412     }
413
414     /* If the symbol is already marked as import, the address size must match.
415      * Apart from that, ignore the global declaration.
416      */
417     if (S->Flags & SF_IMPORT) {
418         if (AddrSize == ADDR_SIZE_DEFAULT) {
419             /* Use the size of the current segment */
420             AddrSize = GetCurrentSegAddrSize ();
421         }
422         if (AddrSize != S->AddrSize) {
423             Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
424         }
425         return;
426     }
427
428     /* If the symbol is already an export: If it is not defined, the address
429      * sizes must match.
430      */
431     if (S->Flags & SF_EXPORT) {
432         if ((S->Flags & SF_DEFINED) == 0) {
433             /* Symbol is undefined */
434             if (AddrSize != S->ExportSize) {
435                 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
436             }
437         } else if (AddrSize != ADDR_SIZE_DEFAULT) {
438             /* Symbol is defined and address size given */
439             if (AddrSize != S->ExportSize) {
440                 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
441             }
442         }
443         return;
444     }
445
446     /* If the symbol is already marked as global, the address size must match.
447      * Use the ExportSize here, since it contains the actual address size
448      * passed to this function.
449      */
450     if (S->Flags & SF_GLOBAL) {
451         if (AddrSize != S->ExportSize) {
452             Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
453         }
454         return;
455     }
456
457     /* If we come here, the symbol was neither declared as export, import or
458      * global before. Check if it is already defined, in which case it will
459      * become an export. If it is not defined, mark it as global and remember
460      * the given address sizes.
461      */
462     if (S->Flags & SF_DEFINED) {
463         /* The symbol is defined, export it */
464         S->ExportSize = AddrSize;
465         if (S->ExportSize == ADDR_SIZE_DEFAULT) {
466             /* No export size given, use the real size of the symbol */
467             S->ExportSize = S->AddrSize;
468         } else if (S->AddrSize > S->ExportSize) {
469             /* We're exporting a symbol smaller than it actually is */
470             Warning (1, "Symbol `%m%p' is %s but exported %s",
471                      GetSymName (S), AddrSizeToStr (S->AddrSize),
472                      AddrSizeToStr (S->ExportSize));
473         }
474         S->Flags |= (SF_EXPORT | Flags);
475     } else {
476         /* Since we don't know if the symbol will get exported or imported,
477          * remember two different address sizes: One for an import in AddrSize,
478          * and the other one for an export in ExportSize.
479          */
480         S->AddrSize = AddrSize;
481         if (S->AddrSize == ADDR_SIZE_DEFAULT) {
482             /* Use the size of the current segment */
483             S->AddrSize = GetCurrentSegAddrSize ();
484         }
485         S->ExportSize = AddrSize;
486         S->Flags |= (SF_GLOBAL | Flags);
487     }
488 }
489
490
491
492 void SymConDes (SymEntry* S, unsigned char AddrSize, unsigned Type, unsigned Prio)
493 /* Mark the given symbol as a module constructor/destructor. This will also
494  * mark the symbol as an export. Initializers may never be zero page symbols.
495  */
496 {
497     /* Check the parameters */
498 #if (CD_TYPE_MIN != 0)
499     CHECK (Type >= CD_TYPE_MIN && Type <= CD_TYPE_MAX);
500 #else
501     CHECK (Type <= CD_TYPE_MAX);
502 #endif
503     CHECK (Prio >= CD_PRIO_MIN && Prio <= CD_PRIO_MAX);
504
505     /* Check for errors */
506     if (S->Flags & SF_IMPORT) {
507         /* The symbol is already marked as imported external symbol */
508         Error ("Symbol `%m%p' is already an import", GetSymName (S));
509         return;
510     }
511     if (S->Flags & SF_VAR) {
512         /* Variable symbols cannot be exported or imported */
513         Error ("Var symbol `%m%p' cannot be exported", GetSymName (S));
514         return;
515     }
516
517     /* If the symbol was already marked as an export or global, check if
518      * this was done specifiying the same address size. In case of a global
519      * declaration, silently remove the global flag.
520      */
521     if (S->Flags & (SF_EXPORT | SF_GLOBAL)) {
522         if (S->ExportSize != AddrSize) {
523             Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
524         }
525         S->Flags &= ~SF_GLOBAL;
526     }
527     S->ExportSize = AddrSize;
528
529     /* If the symbol is already defined, check symbol size against the
530      * exported size.
531      */
532     if (S->Flags & SF_DEFINED) {
533         if (S->ExportSize == ADDR_SIZE_DEFAULT) {
534             /* Use the real size of the symbol */
535             S->ExportSize = S->AddrSize;
536         } else if (S->AddrSize != S->ExportSize) {
537             Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
538         }
539     }
540
541     /* If the symbol was already declared as a condes, check if the new
542      * priority value is the same as the old one.
543      */
544     if (S->ConDesPrio[Type] != CD_PRIO_NONE) {
545         if (S->ConDesPrio[Type] != Prio) {
546             Error ("Redeclaration mismatch for symbol `%m%p'", GetSymName (S));
547         }
548     }
549     S->ConDesPrio[Type] = Prio;
550
551     /* Set the symbol data */
552     S->Flags |= (SF_EXPORT | SF_REFERENCED);
553 }
554
555
556
557 void SymGuessedAddrSize (SymEntry* Sym, unsigned char AddrSize)
558 /* Mark the address size of the given symbol as guessed. The address size
559  * passed as argument is the one NOT used, because the actual address size
560  * wasn't known. Example: Zero page addressing was not used because symbol
561  * is undefined, and absolute addressing was available.
562  */
563 {
564     /* We must have a valid address size passed */
565     PRECONDITION (AddrSize != ADDR_SIZE_DEFAULT);
566
567     /* We do not support all address sizes currently */
568     if (AddrSize > sizeof (Sym->GuessedUse) / sizeof (Sym->GuessedUse[0])) {
569         return;
570     }
571
572     /* We can only remember one such occurance */
573     if (Sym->GuessedUse[AddrSize-1]) {
574         return;
575     }
576
577     /* Ok, remember the file position */
578     Sym->GuessedUse[AddrSize-1] = xdup (&CurPos, sizeof (CurPos));
579 }
580
581
582
583 void SymExportFromGlobal (SymEntry* S)
584 /* Called at the end of assembly. Converts a global symbol that is defined
585  * into an export.
586  */
587 {
588     /* Remove the global flag and make the symbol an export */
589     S->Flags &= ~SF_GLOBAL;
590     S->Flags |= SF_EXPORT;
591 }
592
593
594
595 void SymImportFromGlobal (SymEntry* S)
596 /* Called at the end of assembly. Converts a global symbol that is undefined
597  * into an import.
598  */
599 {
600     /* Remove the global flag and make it an import */
601     S->Flags &= ~SF_GLOBAL;
602     S->Flags |= SF_IMPORT;
603 }
604
605
606
607 int SymIsConst (SymEntry* S, long* Val)
608 /* Return true if the given symbol has a constant value. If Val is not NULL
609  * and the symbol has a constant value, store it's value there.
610  */
611 {
612     /* Check for constness */
613     return (SymHasExpr (S) && IsConstExpr (S->Expr, Val));
614 }
615
616
617
618 SymTable* GetSymParentScope (SymEntry* S)
619 /* Get the parent scope of the symbol (not the one it is defined in). Return
620  * NULL if the symbol is a cheap local, or defined on global level.
621  */
622 {
623     if ((S->Flags & SF_LOCAL) != 0) {
624         /* This is a cheap local symbol */
625         return 0;
626     } else {
627         /* This is a global symbol */
628         return S->Sym.Tab->Parent;
629     }
630 }
631
632
633
634 struct ExprNode* GetSymExpr (SymEntry* S)
635 /* Get the expression for a non-const symbol */
636 {
637     PRECONDITION (S != 0 && SymHasExpr (S));
638     return S->Expr;
639 }
640
641
642
643 const struct ExprNode* SymResolve (const SymEntry* S)
644 /* Helper function for DumpExpr. Resolves a symbol into an expression or return
645  * NULL. Do not call in other contexts!
646  */
647 {
648     return SymHasExpr (S)? S->Expr : 0;
649 }
650
651
652
653 long GetSymVal (SymEntry* S)
654 /* Return the value of a symbol assuming it's constant. FAIL will be called
655  * in case the symbol is undefined or not constant.
656  */
657 {
658     long Val;
659     CHECK (S != 0 && SymHasExpr (S) && IsConstExpr (GetSymExpr (S), &Val));
660     return Val;
661 }
662
663
664
665 unsigned GetSymIndex (const SymEntry* S)
666 /* Return the symbol index for the given symbol */
667 {
668     PRECONDITION (S != 0 && (S->Flags & SF_INDEXED) != 0);
669     return S->Index;
670 }
671
672
673