/*****************************************************************************/ /* */ /* symentry.c */ /* */ /* Symbol table entry for the ca65 macroassembler */ /* */ /* */ /* */ /* (C) 1998-2011, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ /* warranty. In no event will the authors be held liable for any damages */ /* arising from the use of this software. */ /* */ /* Permission is granted to anyone to use this software for any purpose, */ /* including commercial applications, and to alter it and redistribute it */ /* freely, subject to the following restrictions: */ /* */ /* 1. The origin of this software must not be misrepresented; you must not */ /* claim that you wrote the original software. If you use this software */ /* in a product, an acknowledgment in the product documentation would be */ /* appreciated but is not required. */ /* 2. Altered source versions must be plainly marked as such, and must not */ /* be misrepresented as being the original software. */ /* 3. This notice may not be removed or altered from any source */ /* distribution. */ /* */ /*****************************************************************************/ #include /* common */ #include "addrsize.h" #include "symdefs.h" #include "xmalloc.h" /* ca65 */ #include "error.h" #include "expr.h" #include "global.h" #include "scanner.h" #include "segment.h" #include "spool.h" #include "studyexpr.h" /* ### */ #include "symentry.h" #include "symtab.h" /*****************************************************************************/ /* Data */ /*****************************************************************************/ /* List of all symbol table entries */ SymEntry* SymList = 0; /* Pointer to last defined symbol */ SymEntry* SymLast = 0; /*****************************************************************************/ /* Code */ /*****************************************************************************/ SymEntry* NewSymEntry (const StrBuf* Name, unsigned Flags) /* Allocate a symbol table entry, initialize and return it */ { unsigned I; /* Allocate memory */ SymEntry* S = xmalloc (sizeof (SymEntry)); /* Initialize the entry */ S->Left = 0; S->Right = 0; S->Locals = 0; S->Sym.Tab = 0; S->DefLines = EmptyCollection; S->RefLines = EmptyCollection; for (I = 0; I < sizeof (S->GuessedUse) / sizeof (S->GuessedUse[0]); ++I) { S->GuessedUse[I] = 0; } S->HLLSym = 0; S->Flags = Flags; S->DebugSymId = ~0U; S->ImportId = ~0U; S->ExportId = ~0U; S->Expr = 0; S->ExprRefs = AUTO_COLLECTION_INITIALIZER; S->ExportSize = ADDR_SIZE_DEFAULT; S->AddrSize = ADDR_SIZE_DEFAULT; memset (S->ConDesPrio, 0, sizeof (S->ConDesPrio)); S->Name = GetStrBufId (Name); /* Insert it into the list of all entries */ S->List = SymList; SymList = S; /* Return the initialized entry */ return S; } int SymSearchTree (SymEntry* T, const StrBuf* Name, SymEntry** E) /* Search in the given tree for a name. If we find the symbol, the function ** will return 0 and put the entry pointer into E. If we did not find the ** symbol, and the tree is empty, E is set to NULL. If the tree is not empty, ** E will be set to the last entry, and the result of the function is <0 if ** the entry should be inserted on the left side, and >0 if it should get ** inserted on the right side. */ { /* Is there a tree? */ if (T == 0) { *E = 0; return 1; } /* We have a table, search it */ while (1) { /* Get the symbol name */ const StrBuf* SymName = GetStrBuf (T->Name); /* Choose next entry */ int Cmp = SB_Compare (Name, SymName); if (Cmp < 0 && T->Left) { T = T->Left; } else if (Cmp > 0 && T->Right) { T = T->Right; } else { /* Found or end of search, return the result */ *E = T; return Cmp; } } } void SymTransferExprRefs (SymEntry* From, SymEntry* To) /* Transfer all expression references from one symbol to another. */ { unsigned I; for (I = 0; I < CollCount (&From->ExprRefs); ++I) { /* Get the expression node */ ExprNode* E = CollAtUnchecked (&From->ExprRefs, I); /* Safety */ CHECK (E->Op == EXPR_SYMBOL && E->V.Sym == From); /* Replace the symbol reference */ E->V.Sym = To; /* Add the expression reference */ SymAddExprRef (To, E); } /* Remove all symbol references from the old symbol */ CollDeleteAll (&From->ExprRefs); } static void SymReplaceExprRefs (SymEntry* S) /* Replace the references to this symbol by a copy of the symbol expression */ { unsigned I; long Val; /* Check if the expression is const and get its value */ int IsConst = IsConstExpr (S->Expr, &Val); CHECK (IsConst); /* Loop over all references */ for (I = 0; I < CollCount (&S->ExprRefs); ++I) { /* Get the expression node */ ExprNode* E = CollAtUnchecked (&S->ExprRefs, I); /* Safety */ CHECK (E->Op == EXPR_SYMBOL && E->V.Sym == S); /* We cannot touch the root node, since there are pointers to it. ** Replace it by a literal node. */ E->Op = EXPR_LITERAL; E->V.IVal = Val; } /* Remove all symbol references from the symbol */ CollDeleteAll (&S->ExprRefs); } void SymDef (SymEntry* S, ExprNode* Expr, unsigned char AddrSize, unsigned Flags) /* Define a new symbol */ { if (S->Flags & SF_IMPORT) { /* Defined symbol is marked as imported external symbol */ Error ("Symbol '%m%p' is already an import", GetSymName (S)); return; } if ((Flags & SF_VAR) != 0 && (S->Flags & (SF_EXPORT | SF_GLOBAL))) { /* Variable symbols cannot be exports or globals */ Error ("Var symbol '%m%p' cannot be an export or global symbol", GetSymName (S)); return; } if (S->Flags & SF_DEFINED) { /* Multiple definition. In case of a variable, this is legal. */ if ((S->Flags & SF_VAR) == 0) { Error ("Symbol '%m%p' is already defined", GetSymName (S)); S->Flags |= SF_MULTDEF; return; } else { /* Redefinition must also be a variable symbol */ if ((Flags & SF_VAR) == 0) { Error ("Symbol '%m%p' is already different kind", GetSymName (S)); return; } /* Delete the current symbol expression, since it will get ** replaced */ FreeExpr (S->Expr); S->Expr = 0; } } /* Map a default address size to a real value */ if (AddrSize == ADDR_SIZE_DEFAULT) { /* ### Must go! Delay address size calculation until end of assembly! */ ExprDesc ED; ED_Init (&ED); StudyExpr (Expr, &ED); AddrSize = ED.AddrSize; ED_Done (&ED); } /* Set the symbol value */ S->Expr = Expr; /* In case of a variable symbol, walk over all expressions containing ** this symbol and replace the (sub-)expression by the literal value of ** the tree. Be sure to replace the expression node in place, since there ** may be pointers to it. */ if (Flags & SF_VAR) { SymReplaceExprRefs (S); } /* If the symbol is marked as global, export it. Address size is checked ** below. */ if (S->Flags & SF_GLOBAL) { S->Flags = (S->Flags & ~SF_GLOBAL) | SF_EXPORT; ReleaseFullLineInfo (&S->DefLines); } /* Mark the symbol as defined and use the given address size */ S->Flags |= (SF_DEFINED | Flags); S->AddrSize = AddrSize; /* Remember the line info of the symbol definition */ GetFullLineInfo (&S->DefLines); /* If the symbol is exported, check the address sizes */ if (S->Flags & SF_EXPORT) { if (S->ExportSize == ADDR_SIZE_DEFAULT) { /* Use the real size of the symbol */ S->ExportSize = S->AddrSize; } else if (S->AddrSize > S->ExportSize) { /* We're exporting a symbol smaller than it actually is */ Warning (1, "Symbol '%m%p' is %s but exported %s", GetSymName (S), AddrSizeToStr (S->AddrSize), AddrSizeToStr (S->ExportSize)); } } /* If this is not a local symbol, remember it as the last global one */ if ((S->Flags & SF_LOCAL) == 0) { SymLast = S; } } void SymRef (SymEntry* S) /* Mark the given symbol as referenced */ { /* Mark the symbol as referenced */ S->Flags |= SF_REFERENCED; /* Remember the current location */ CollAppend (&S->RefLines, GetAsmLineInfo ()); } void SymImport (SymEntry* S, unsigned char AddrSize, unsigned Flags) /* Mark the given symbol as an imported symbol */ { if (S->Flags & SF_DEFINED) { Error ("Symbol '%m%p' is already defined", GetSymName (S)); S->Flags |= SF_MULTDEF; return; } if (S->Flags & SF_EXPORT) { /* The symbol is already marked as exported symbol */ Error ("Cannot import exported symbol '%m%p'", GetSymName (S)); return; } /* If no address size is given, use the address size of the enclosing ** segment. */ if (AddrSize == ADDR_SIZE_DEFAULT) { AddrSize = GetCurrentSegAddrSize (); } /* If the symbol is marked as import or global, check the address size, ** then do silently remove the global flag. */ if (S->Flags & SF_IMPORT) { if ((Flags & SF_FORCED) != (S->Flags & SF_FORCED)) { Error ("Redeclaration mismatch for symbol '%m%p'", GetSymName (S)); } if (AddrSize != S->AddrSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } } if (S->Flags & SF_GLOBAL) { S->Flags &= ~SF_GLOBAL; if (AddrSize != S->AddrSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } } /* Set the symbol data */ S->Flags |= (SF_IMPORT | Flags); S->AddrSize = AddrSize; /* Mark the position of the import as the position of the definition. ** Please note: In case of multiple .global or .import statements, the line ** infos add up. */ GetFullLineInfo (&S->DefLines); } void SymExport (SymEntry* S, unsigned char AddrSize, unsigned Flags) /* Mark the given symbol as an exported symbol */ { /* Check if it's ok to export the symbol */ if (S->Flags & SF_IMPORT) { /* The symbol is already marked as imported external symbol */ Error ("Symbol '%m%p' is already an import", GetSymName (S)); return; } if (S->Flags & SF_VAR) { /* Variable symbols cannot be exported */ Error ("Var symbol '%m%p' cannot be exported", GetSymName (S)); return; } /* If the symbol was marked as global before, remove the global flag and ** proceed, but check the address size. */ if (S->Flags & SF_GLOBAL) { if (AddrSize != S->ExportSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } S->Flags &= ~SF_GLOBAL; /* .GLOBAL remembers line infos in case an .IMPORT follows. We have ** to remove these here. */ ReleaseFullLineInfo (&S->DefLines); } /* If the symbol was already marked as an export, but wasn't defined ** before, the address sizes in both definitions must match. */ if ((S->Flags & (SF_EXPORT|SF_DEFINED)) == SF_EXPORT) { if (S->ExportSize != AddrSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } } S->ExportSize = AddrSize; /* If the symbol is already defined, check symbol size against the ** exported size. */ if (S->Flags & SF_DEFINED) { if (S->ExportSize == ADDR_SIZE_DEFAULT) { /* No export size given, use the real size of the symbol */ S->ExportSize = S->AddrSize; } else if (S->AddrSize > S->ExportSize) { /* We're exporting a symbol smaller than it actually is */ Warning (1, "Symbol '%m%p' is %s but exported %s", GetSymName (S), AddrSizeToStr (S->AddrSize), AddrSizeToStr (S->ExportSize)); } } /* Set the symbol data */ S->Flags |= (SF_EXPORT | SF_REFERENCED | Flags); /* Remember line info for this reference */ CollAppend (&S->RefLines, GetAsmLineInfo ()); } void SymGlobal (SymEntry* S, unsigned char AddrSize, unsigned Flags) /* Mark the given symbol as a global symbol, that is, as a symbol that is ** either imported or exported. */ { if (S->Flags & SF_VAR) { /* Variable symbols cannot be exported or imported */ Error ("Var symbol '%m%p' cannot be made global", GetSymName (S)); return; } /* If the symbol is already marked as import, the address size must match. ** Apart from that, ignore the global declaration. */ if (S->Flags & SF_IMPORT) { if (AddrSize == ADDR_SIZE_DEFAULT) { /* Use the size of the current segment */ AddrSize = GetCurrentSegAddrSize (); } if (AddrSize != S->AddrSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } return; } /* If the symbol is already an export: If it is not defined, the address ** sizes must match. */ if (S->Flags & SF_EXPORT) { if ((S->Flags & SF_DEFINED) == 0) { /* Symbol is undefined */ if (AddrSize != S->ExportSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } } else if (AddrSize != ADDR_SIZE_DEFAULT) { /* Symbol is defined and address size given */ if (AddrSize != S->ExportSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } } return; } /* If the symbol is already marked as global, the address size must match. ** Use the ExportSize here, since it contains the actual address size ** passed to this function. */ if (S->Flags & SF_GLOBAL) { if (AddrSize != S->ExportSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } return; } /* If we come here, the symbol was neither declared as export, import or ** global before. Check if it is already defined, in which case it will ** become an export. If it is not defined, mark it as global and remember ** the given address sizes. */ if (S->Flags & SF_DEFINED) { /* The symbol is defined, export it */ S->ExportSize = AddrSize; if (S->ExportSize == ADDR_SIZE_DEFAULT) { /* No export size given, use the real size of the symbol */ S->ExportSize = S->AddrSize; } else if (S->AddrSize > S->ExportSize) { /* We're exporting a symbol smaller than it actually is */ Warning (1, "Symbol '%m%p' is %s but exported %s", GetSymName (S), AddrSizeToStr (S->AddrSize), AddrSizeToStr (S->ExportSize)); } S->Flags |= (SF_EXPORT | Flags); } else { /* Since we don't know if the symbol will get exported or imported, ** remember two different address sizes: One for an import in AddrSize, ** and the other one for an export in ExportSize. */ S->AddrSize = AddrSize; if (S->AddrSize == ADDR_SIZE_DEFAULT) { /* Use the size of the current segment */ S->AddrSize = GetCurrentSegAddrSize (); } S->ExportSize = AddrSize; S->Flags |= (SF_GLOBAL | Flags); /* Remember the current location as location of definition in case ** an .IMPORT follows later. */ GetFullLineInfo (&S->DefLines); } } void SymConDes (SymEntry* S, unsigned char AddrSize, unsigned Type, unsigned Prio) /* Mark the given symbol as a module constructor/destructor. This will also ** mark the symbol as an export. Initializers may never be zero page symbols. */ { /* Check the parameters */ #if (CD_TYPE_MIN != 0) CHECK (Type >= CD_TYPE_MIN && Type <= CD_TYPE_MAX); #else CHECK (Type <= CD_TYPE_MAX); #endif CHECK (Prio >= CD_PRIO_MIN && Prio <= CD_PRIO_MAX); /* Check for errors */ if (S->Flags & SF_IMPORT) { /* The symbol is already marked as imported external symbol */ Error ("Symbol '%m%p' is already an import", GetSymName (S)); return; } if (S->Flags & SF_VAR) { /* Variable symbols cannot be exported or imported */ Error ("Var symbol '%m%p' cannot be exported", GetSymName (S)); return; } /* If the symbol is already defined, check symbol size against the ** exported size. */ if (S->Flags & SF_DEFINED) { if (AddrSize == ADDR_SIZE_DEFAULT) { /* Use the real size of the symbol */ AddrSize = S->AddrSize; } else if (S->AddrSize != AddrSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } } /* If the symbol was already marked as an export or global, check if ** this was done specifiying the same address size. In case of a global ** declaration, silently remove the global flag. */ if (S->Flags & (SF_EXPORT | SF_GLOBAL)) { if (S->ExportSize != AddrSize) { Error ("Address size mismatch for symbol '%m%p'", GetSymName (S)); } S->Flags &= ~SF_GLOBAL; } S->ExportSize = AddrSize; /* If the symbol already was declared as a condes of this type, ** check if the new priority value is the same as the old one. */ if (S->ConDesPrio[Type] != CD_PRIO_NONE) { if (S->ConDesPrio[Type] != Prio) { Error ("Redeclaration mismatch for symbol '%m%p'", GetSymName (S)); } } S->ConDesPrio[Type] = Prio; /* Set the symbol data */ S->Flags |= (SF_EXPORT | SF_REFERENCED); /* Remember the line info for this reference */ CollAppend (&S->RefLines, GetAsmLineInfo ()); } void SymGuessedAddrSize (SymEntry* Sym, unsigned char AddrSize) /* Mark the address size of the given symbol as guessed. The address size ** passed as argument is the one NOT used, because the actual address size ** wasn't known. Example: Zero page addressing was not used because symbol ** is undefined, and absolute addressing was available. */ { /* We must have a valid address size passed */ PRECONDITION (AddrSize != ADDR_SIZE_DEFAULT); /* We do not support all address sizes currently */ if (AddrSize > sizeof (Sym->GuessedUse) / sizeof (Sym->GuessedUse[0])) { return; } /* We can only remember one such occurance */ if (Sym->GuessedUse[AddrSize-1]) { return; } /* Ok, remember the file position */ Sym->GuessedUse[AddrSize-1] = xdup (&CurTok.Pos, sizeof (CurTok.Pos)); } void SymExportFromGlobal (SymEntry* S) /* Called at the end of assembly. Converts a global symbol that is defined ** into an export. */ { /* Remove the global flag and make the symbol an export */ S->Flags &= ~SF_GLOBAL; S->Flags |= SF_EXPORT; } void SymImportFromGlobal (SymEntry* S) /* Called at the end of assembly. Converts a global symbol that is undefined ** into an import. */ { /* Remove the global flag and make it an import */ S->Flags &= ~SF_GLOBAL; S->Flags |= SF_IMPORT; } int SymIsConst (const SymEntry* S, long* Val) /* Return true if the given symbol has a constant value. If Val is not NULL ** and the symbol has a constant value, store it's value there. */ { /* Check for constness */ return (SymHasExpr (S) && IsConstExpr (S->Expr, Val)); } SymTable* GetSymParentScope (SymEntry* S) /* Get the parent scope of the symbol (not the one it is defined in). Return ** NULL if the symbol is a cheap local, or defined on global level. */ { if ((S->Flags & SF_LOCAL) != 0) { /* This is a cheap local symbol */ return 0; } else if (S->Sym.Tab == 0) { /* Symbol not in a table. This may happen if there have been errors ** before. Return NULL in this case to avoid further errors. */ return 0; } else { /* This is a global symbol */ return S->Sym.Tab->Parent; } } struct ExprNode* GetSymExpr (SymEntry* S) /* Get the expression for a non-const symbol */ { PRECONDITION (S != 0 && SymHasExpr (S)); return S->Expr; } const struct ExprNode* SymResolve (const SymEntry* S) /* Helper function for DumpExpr. Resolves a symbol into an expression or return ** NULL. Do not call in other contexts! */ { return SymHasExpr (S)? S->Expr : 0; } long GetSymVal (SymEntry* S) /* Return the value of a symbol assuming it's constant. FAIL will be called ** in case the symbol is undefined or not constant. */ { long Val; CHECK (S != 0 && SymHasExpr (S) && IsConstExpr (GetSymExpr (S), &Val)); return Val; } unsigned GetSymImportId (const SymEntry* S) /* Return the import id for the given symbol */ { PRECONDITION (S != 0 && (S->Flags & SF_IMPORT) && S->ImportId != ~0U); return S->ImportId; } unsigned GetSymExportId (const SymEntry* S) /* Return the export id for the given symbol */ { PRECONDITION (S != 0 && (S->Flags & SF_EXPORT) && S->ExportId != ~0U); return S->ExportId; } unsigned GetSymInfoFlags (const SymEntry* S, long* ConstVal) /* Return a set of flags used when writing symbol information into a file. ** If the SYM_CONST bit is set, ConstVal will contain the constant value ** of the symbol. The result does not include the condes count. ** See common/symdefs.h for more information. */ { /* Setup info flags */ unsigned Flags = 0; Flags |= SymIsConst (S, ConstVal)? SYM_CONST : SYM_EXPR; Flags |= (S->Flags & SF_LABEL)? SYM_LABEL : SYM_EQUATE; Flags |= (S->Flags & SF_LOCAL)? SYM_CHEAP_LOCAL : SYM_STD; if (S->Flags & SF_EXPORT) { Flags |= SYM_EXPORT; } if (S->Flags & SF_IMPORT) { Flags |= SYM_IMPORT; } /* Return the result */ return Flags; }