1 /*****************************************************************************/
5 /* Symbol table entry for the ca65 macroassembler */
9 /* (C) 1998-2011, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
50 #include "studyexpr.h" /* ### */
56 /*****************************************************************************/
58 /*****************************************************************************/
62 /* List of all symbol table entries */
63 SymEntry* SymList = 0;
65 /* Pointer to last defined symbol */
66 SymEntry* SymLast = 0;
70 /*****************************************************************************/
72 /*****************************************************************************/
76 SymEntry* NewSymEntry (const StrBuf* Name, unsigned Flags)
77 /* Allocate a symbol table entry, initialize and return it */
82 SymEntry* S = xmalloc (sizeof (SymEntry));
84 /* Initialize the entry */
89 S->DefLines = EmptyCollection;
90 S->RefLines = EmptyCollection;
91 for (I = 0; I < sizeof (S->GuessedUse) / sizeof (S->GuessedUse[0]); ++I) {
99 S->ExprRefs = AUTO_COLLECTION_INITIALIZER;
100 S->ExportSize = ADDR_SIZE_DEFAULT;
101 S->AddrSize = ADDR_SIZE_DEFAULT;
102 memset (S->ConDesPrio, 0, sizeof (S->ConDesPrio));
103 S->Name = GetStrBufId (Name);
105 /* Insert it into the list of all entries */
109 /* Return the initialized entry */
115 int SymSearchTree (SymEntry* T, const StrBuf* Name, SymEntry** E)
116 /* Search in the given tree for a name. If we find the symbol, the function
117 * will return 0 and put the entry pointer into E. If we did not find the
118 * symbol, and the tree is empty, E is set to NULL. If the tree is not empty,
119 * E will be set to the last entry, and the result of the function is <0 if
120 * the entry should be inserted on the left side, and >0 if it should get
121 * inserted on the right side.
124 /* Is there a tree? */
130 /* We have a table, search it */
133 /* Get the symbol name */
134 const StrBuf* SymName = GetStrBuf (T->Name);
136 /* Choose next entry */
137 int Cmp = SB_Compare (Name, SymName);
138 if (Cmp < 0 && T->Left) {
140 } else if (Cmp > 0&& T->Right) {
143 /* Found or end of search, return the result */
152 void SymTransferExprRefs (SymEntry* From, SymEntry* To)
153 /* Transfer all expression references from one symbol to another. */
157 for (I = 0; I < CollCount (&From->ExprRefs); ++I) {
159 /* Get the expression node */
160 ExprNode* E = CollAtUnchecked (&From->ExprRefs, I);
163 CHECK (E->Op == EXPR_SYMBOL && E->V.Sym == From);
165 /* Replace the symbol reference */
168 /* Add the expression reference */
169 SymAddExprRef (To, E);
172 /* Remove all symbol references from the old symbol */
173 CollDeleteAll (&From->ExprRefs);
178 static void SymReplaceExprRefs (SymEntry* S)
179 /* Replace the references to this symbol by a copy of the symbol expression */
184 /* Check if the expression is const and get its value */
185 int IsConst = IsConstExpr (S->Expr, &Val);
188 /* Loop over all references */
189 for (I = 0; I < CollCount (&S->ExprRefs); ++I) {
191 /* Get the expression node */
192 ExprNode* E = CollAtUnchecked (&S->ExprRefs, I);
195 CHECK (E->Op == EXPR_SYMBOL && E->V.Sym == S);
197 /* We cannot touch the root node, since there are pointers to it.
198 * Replace it by a literal node.
200 E->Op = EXPR_LITERAL;
204 /* Remove all symbol references from the symbol */
205 CollDeleteAll (&S->ExprRefs);
210 void SymDef (SymEntry* S, ExprNode* Expr, unsigned char AddrSize, unsigned Flags)
211 /* Define a new symbol */
213 if (S->Flags & SF_IMPORT) {
214 /* Defined symbol is marked as imported external symbol */
215 Error ("Symbol `%m%p' is already an import", GetSymName (S));
218 if ((Flags & SF_VAR) != 0 && (S->Flags & (SF_EXPORT | SF_GLOBAL))) {
219 /* Variable symbols cannot be exports or globals */
220 Error ("Var symbol `%m%p' cannot be an export or global symbol", GetSymName (S));
223 if (S->Flags & SF_DEFINED) {
224 /* Multiple definition. In case of a variable, this is legal. */
225 if ((S->Flags & SF_VAR) == 0) {
226 Error ("Symbol `%m%p' is already defined", GetSymName (S));
227 S->Flags |= SF_MULTDEF;
230 /* Redefinition must also be a variable symbol */
231 if ((Flags & SF_VAR) == 0) {
232 Error ("Symbol `%m%p' is already different kind", GetSymName (S));
235 /* Delete the current symbol expression, since it will get
243 /* Map a default address size to a real value */
244 if (AddrSize == ADDR_SIZE_DEFAULT) {
245 /* ### Must go! Delay address size calculation until end of assembly! */
248 StudyExpr (Expr, &ED);
249 AddrSize = ED.AddrSize;
253 /* Set the symbol value */
256 /* In case of a variable symbol, walk over all expressions containing
257 * this symbol and replace the (sub-)expression by the literal value of
258 * the tree. Be sure to replace the expression node in place, since there
259 * may be pointers to it.
261 if (Flags & SF_VAR) {
262 SymReplaceExprRefs (S);
265 /* If the symbol is marked as global, export it. Address size is checked
268 if (S->Flags & SF_GLOBAL) {
269 S->Flags = (S->Flags & ~SF_GLOBAL) | SF_EXPORT;
270 ReleaseFullLineInfo (&S->DefLines);
273 /* Mark the symbol as defined and use the given address size */
274 S->Flags |= (SF_DEFINED | Flags);
275 S->AddrSize = AddrSize;
277 /* Remember the line info of the symbol definition */
278 GetFullLineInfo (&S->DefLines);
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 Warning (1, "Symbol `%m%p' is %s but exported %s",
288 GetSymName (S), AddrSizeToStr (S->AddrSize),
289 AddrSizeToStr (S->ExportSize));
293 /* If this is not a local symbol, remember it as the last global one */
294 if ((S->Flags & SF_LOCAL) == 0) {
301 void SymRef (SymEntry* S)
302 /* Mark the given symbol as referenced */
304 /* Mark the symbol as referenced */
305 S->Flags |= SF_REFERENCED;
307 /* Remember the current location */
308 CollAppend (&S->RefLines, GetAsmLineInfo ());
313 void SymImport (SymEntry* S, unsigned char AddrSize, unsigned Flags)
314 /* Mark the given symbol as an imported symbol */
316 if (S->Flags & SF_DEFINED) {
317 Error ("Symbol `%m%p' is already defined", GetSymName (S));
318 S->Flags |= SF_MULTDEF;
321 if (S->Flags & SF_EXPORT) {
322 /* The symbol is already marked as exported symbol */
323 Error ("Cannot import exported symbol `%m%p'", GetSymName (S));
327 /* If no address size is given, use the address size of the enclosing
330 if (AddrSize == ADDR_SIZE_DEFAULT) {
331 AddrSize = GetCurrentSegAddrSize ();
334 /* If the symbol is marked as import or global, check the address size,
335 * then do silently remove the global flag.
337 if (S->Flags & SF_IMPORT) {
338 if ((Flags & SF_FORCED) != (S->Flags & SF_FORCED)) {
339 Error ("Redeclaration mismatch for symbol `%m%p'", GetSymName (S));
341 if (AddrSize != S->AddrSize) {
342 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
345 if (S->Flags & SF_GLOBAL) {
346 S->Flags &= ~SF_GLOBAL;
347 if (AddrSize != S->AddrSize) {
348 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
352 /* Set the symbol data */
353 S->Flags |= (SF_IMPORT | Flags);
354 S->AddrSize = AddrSize;
356 /* Mark the position of the import as the position of the definition.
357 * Please note: In case of multiple .global or .import statements, the line
360 GetFullLineInfo (&S->DefLines);
365 void SymExport (SymEntry* S, unsigned char AddrSize, unsigned Flags)
366 /* Mark the given symbol as an exported symbol */
368 /* Check if it's ok to export the symbol */
369 if (S->Flags & SF_IMPORT) {
370 /* The symbol is already marked as imported external symbol */
371 Error ("Symbol `%m%p' is already an import", GetSymName (S));
374 if (S->Flags & SF_VAR) {
375 /* Variable symbols cannot be exported */
376 Error ("Var symbol `%m%p' cannot be exported", GetSymName (S));
380 /* If the symbol was marked as global before, remove the global flag and
381 * proceed, but check the address size.
383 if (S->Flags & SF_GLOBAL) {
384 if (AddrSize != S->ExportSize) {
385 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
387 S->Flags &= ~SF_GLOBAL;
389 /* .GLOBAL remembers line infos in case an .IMPORT follows. We have
390 * to remove these here.
392 ReleaseFullLineInfo (&S->DefLines);
395 /* If the symbol was already marked as an export, but wasn't defined
396 * before, the address sizes in both definitions must match.
398 if ((S->Flags & (SF_EXPORT|SF_DEFINED)) == SF_EXPORT) {
399 if (S->ExportSize != AddrSize) {
400 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
403 S->ExportSize = AddrSize;
405 /* If the symbol is already defined, check symbol size against the
408 if (S->Flags & SF_DEFINED) {
409 if (S->ExportSize == ADDR_SIZE_DEFAULT) {
410 /* No export size given, use the real size of the symbol */
411 S->ExportSize = S->AddrSize;
412 } else if (S->AddrSize > S->ExportSize) {
413 /* We're exporting a symbol smaller than it actually is */
414 Warning (1, "Symbol `%m%p' is %s but exported %s",
415 GetSymName (S), AddrSizeToStr (S->AddrSize),
416 AddrSizeToStr (S->ExportSize));
420 /* Set the symbol data */
421 S->Flags |= (SF_EXPORT | SF_REFERENCED | Flags);
426 void SymGlobal (SymEntry* S, unsigned char AddrSize, unsigned Flags)
427 /* Mark the given symbol as a global symbol, that is, as a symbol that is
428 * either imported or exported.
431 if (S->Flags & SF_VAR) {
432 /* Variable symbols cannot be exported or imported */
433 Error ("Var symbol `%m%p' cannot be made global", GetSymName (S));
437 /* If the symbol is already marked as import, the address size must match.
438 * Apart from that, ignore the global declaration.
440 if (S->Flags & SF_IMPORT) {
441 if (AddrSize == ADDR_SIZE_DEFAULT) {
442 /* Use the size of the current segment */
443 AddrSize = GetCurrentSegAddrSize ();
445 if (AddrSize != S->AddrSize) {
446 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
451 /* If the symbol is already an export: If it is not defined, the address
454 if (S->Flags & SF_EXPORT) {
455 if ((S->Flags & SF_DEFINED) == 0) {
456 /* Symbol is undefined */
457 if (AddrSize != S->ExportSize) {
458 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
460 } else if (AddrSize != ADDR_SIZE_DEFAULT) {
461 /* Symbol is defined and address size given */
462 if (AddrSize != S->ExportSize) {
463 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
469 /* If the symbol is already marked as global, the address size must match.
470 * Use the ExportSize here, since it contains the actual address size
471 * passed to this function.
473 if (S->Flags & SF_GLOBAL) {
474 if (AddrSize != S->ExportSize) {
475 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
480 /* If we come here, the symbol was neither declared as export, import or
481 * global before. Check if it is already defined, in which case it will
482 * become an export. If it is not defined, mark it as global and remember
483 * the given address sizes.
485 if (S->Flags & SF_DEFINED) {
486 /* The symbol is defined, export it */
487 S->ExportSize = AddrSize;
488 if (S->ExportSize == ADDR_SIZE_DEFAULT) {
489 /* No export size given, use the real size of the symbol */
490 S->ExportSize = S->AddrSize;
491 } else if (S->AddrSize > S->ExportSize) {
492 /* We're exporting a symbol smaller than it actually is */
493 Warning (1, "Symbol `%m%p' is %s but exported %s",
494 GetSymName (S), AddrSizeToStr (S->AddrSize),
495 AddrSizeToStr (S->ExportSize));
497 S->Flags |= (SF_EXPORT | Flags);
499 /* Since we don't know if the symbol will get exported or imported,
500 * remember two different address sizes: One for an import in AddrSize,
501 * and the other one for an export in ExportSize.
503 S->AddrSize = AddrSize;
504 if (S->AddrSize == ADDR_SIZE_DEFAULT) {
505 /* Use the size of the current segment */
506 S->AddrSize = GetCurrentSegAddrSize ();
508 S->ExportSize = AddrSize;
509 S->Flags |= (SF_GLOBAL | Flags);
511 /* Remember the current location as location of definition in case
512 * an .IMPORT follows later.
514 GetFullLineInfo (&S->DefLines);
520 void SymConDes (SymEntry* S, unsigned char AddrSize, unsigned Type, unsigned Prio)
521 /* Mark the given symbol as a module constructor/destructor. This will also
522 * mark the symbol as an export. Initializers may never be zero page symbols.
525 /* Check the parameters */
526 #if (CD_TYPE_MIN != 0)
527 CHECK (Type >= CD_TYPE_MIN && Type <= CD_TYPE_MAX);
529 CHECK (Type <= CD_TYPE_MAX);
531 CHECK (Prio >= CD_PRIO_MIN && Prio <= CD_PRIO_MAX);
533 /* Check for errors */
534 if (S->Flags & SF_IMPORT) {
535 /* The symbol is already marked as imported external symbol */
536 Error ("Symbol `%m%p' is already an import", GetSymName (S));
539 if (S->Flags & SF_VAR) {
540 /* Variable symbols cannot be exported or imported */
541 Error ("Var symbol `%m%p' cannot be exported", GetSymName (S));
545 /* If the symbol was already marked as an export or global, check if
546 * this was done specifiying the same address size. In case of a global
547 * declaration, silently remove the global flag.
549 if (S->Flags & (SF_EXPORT | SF_GLOBAL)) {
550 if (S->ExportSize != AddrSize) {
551 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
553 S->Flags &= ~SF_GLOBAL;
555 S->ExportSize = AddrSize;
557 /* If the symbol is already defined, check symbol size against the
560 if (S->Flags & SF_DEFINED) {
561 if (S->ExportSize == ADDR_SIZE_DEFAULT) {
562 /* Use the real size of the symbol */
563 S->ExportSize = S->AddrSize;
564 } else if (S->AddrSize != S->ExportSize) {
565 Error ("Address size mismatch for symbol `%m%p'", GetSymName (S));
569 /* If the symbol was already declared as a condes, check if the new
570 * priority value is the same as the old one.
572 if (S->ConDesPrio[Type] != CD_PRIO_NONE) {
573 if (S->ConDesPrio[Type] != Prio) {
574 Error ("Redeclaration mismatch for symbol `%m%p'", GetSymName (S));
577 S->ConDesPrio[Type] = Prio;
579 /* Set the symbol data */
580 S->Flags |= (SF_EXPORT | SF_REFERENCED);
582 /* In case we have no line info for the definition, record it now */
583 if (CollCount (&S->DefLines) == 0) {
584 GetFullLineInfo (&S->DefLines);
590 void SymGuessedAddrSize (SymEntry* Sym, unsigned char AddrSize)
591 /* Mark the address size of the given symbol as guessed. The address size
592 * passed as argument is the one NOT used, because the actual address size
593 * wasn't known. Example: Zero page addressing was not used because symbol
594 * is undefined, and absolute addressing was available.
597 /* We must have a valid address size passed */
598 PRECONDITION (AddrSize != ADDR_SIZE_DEFAULT);
600 /* We do not support all address sizes currently */
601 if (AddrSize > sizeof (Sym->GuessedUse) / sizeof (Sym->GuessedUse[0])) {
605 /* We can only remember one such occurance */
606 if (Sym->GuessedUse[AddrSize-1]) {
610 /* Ok, remember the file position */
611 Sym->GuessedUse[AddrSize-1] = xdup (&CurTok.Pos, sizeof (CurTok.Pos));
616 void SymExportFromGlobal (SymEntry* S)
617 /* Called at the end of assembly. Converts a global symbol that is defined
621 /* Remove the global flag and make the symbol an export */
622 S->Flags &= ~SF_GLOBAL;
623 S->Flags |= SF_EXPORT;
628 void SymImportFromGlobal (SymEntry* S)
629 /* Called at the end of assembly. Converts a global symbol that is undefined
633 /* Remove the global flag and make it an import */
634 S->Flags &= ~SF_GLOBAL;
635 S->Flags |= SF_IMPORT;
640 int SymIsConst (const SymEntry* S, long* Val)
641 /* Return true if the given symbol has a constant value. If Val is not NULL
642 * and the symbol has a constant value, store it's value there.
645 /* Check for constness */
646 return (SymHasExpr (S) && IsConstExpr (S->Expr, Val));
651 SymTable* GetSymParentScope (SymEntry* S)
652 /* Get the parent scope of the symbol (not the one it is defined in). Return
653 * NULL if the symbol is a cheap local, or defined on global level.
656 if ((S->Flags & SF_LOCAL) != 0) {
657 /* This is a cheap local symbol */
659 } else if (S->Sym.Tab == 0) {
660 /* Symbol not in a table. This may happen if there have been errors
661 * before. Return NULL in this case to avoid further errors.
665 /* This is a global symbol */
666 return S->Sym.Tab->Parent;
672 struct ExprNode* GetSymExpr (SymEntry* S)
673 /* Get the expression for a non-const symbol */
675 PRECONDITION (S != 0 && SymHasExpr (S));
681 const struct ExprNode* SymResolve (const SymEntry* S)
682 /* Helper function for DumpExpr. Resolves a symbol into an expression or return
683 * NULL. Do not call in other contexts!
686 return SymHasExpr (S)? S->Expr : 0;
691 long GetSymVal (SymEntry* S)
692 /* Return the value of a symbol assuming it's constant. FAIL will be called
693 * in case the symbol is undefined or not constant.
697 CHECK (S != 0 && SymHasExpr (S) && IsConstExpr (GetSymExpr (S), &Val));
703 unsigned GetSymImportId (const SymEntry* S)
704 /* Return the import id for the given symbol */
706 PRECONDITION (S != 0 && (S->Flags & SF_IMPORT) && S->ImportId != ~0U);
712 unsigned GetSymExportId (const SymEntry* S)
713 /* Return the export id for the given symbol */
715 PRECONDITION (S != 0 && (S->Flags & SF_EXPORT) && S->ExportId != ~0U);
721 unsigned GetSymInfoFlags (const SymEntry* S, long* ConstVal)
722 /* Return a set of flags used when writing symbol information into a file.
723 * If the SYM_CONST bit is set, ConstVal will contain the constant value
724 * of the symbol. The result does not include the condes count.
725 * See common/symdefs.h for more information.
728 /* Setup info flags */
730 Flags |= SymIsConst (S, ConstVal)? SYM_CONST : SYM_EXPR;
731 Flags |= (S->Flags & SF_LABEL)? SYM_LABEL : SYM_EQUATE;
732 Flags |= (S->Flags & SF_LOCAL)? SYM_CHEAP_LOCAL : SYM_STD;
733 if (S->Flags & SF_EXPORT) {
736 if (S->Flags & SF_IMPORT) {
740 /* Return the result */