1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-28 19:29:53 +00:00
cc65/src/ca65/symtab.c

768 lines
21 KiB
C
Raw Normal View History

/*****************************************************************************/
/* */
/* symtab.c */
/* */
/* Symbol table for the ca65 macroassembler */
/* */
/* */
/* */
/* (C) 1998-2003 Ullrich von Bassewitz */
/* R<>merstra<72>e 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 <string.h>
/* common */
#include "addrsize.h"
#include "check.h"
#include "hashstr.h"
#include "symdefs.h"
#include "xmalloc.h"
/* ca65 */
#include "global.h"
#include "error.h"
#include "expr.h"
#include "objfile.h"
#include "scanner.h"
#include "segment.h"
#include "spool.h"
#include "symtab.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Combined symbol entry flags used within this module */
#define SF_UNDEFMASK (SF_REFERENCED | SF_DEFINED | SF_IMPORT)
#define SF_UNDEFVAL (SF_REFERENCED)
#define SF_EXPMASK (SF_TRAMPOLINE | SF_EXPORT)
#define SF_EXPVAL (SF_EXPORT)
#define SF_DBGINFOMASK (SF_TRAMPOLINE | SF_DEFINED | SF_EXPORT | SF_IMPORT)
#define SF_DBGINFOVAL (SF_DEFINED)
/* Symbol tables */
SymTable* CurrentScope = 0; /* Pointer to current symbol table */
SymTable* RootScope = 0; /* Root symbol table */
/* Symbol table variables */
static unsigned ImportCount = 0;/* Counter for import symbols */
static unsigned ExportCount = 0;/* Counter for export symbols */
/*****************************************************************************/
/* Internally used functions */
/*****************************************************************************/
static unsigned ScopeTableSize (unsigned Level)
/* Get the size of a table for the given lexical level */
{
switch (Level) {
case 0: return 213;
case 1: return 53;
default: return 29;
}
}
static SymTable* NewSymTable (SymTable* Parent, const char* Name)
/* Allocate a symbol table on the heap and return it */
{
/* Determine the lexical level and the number of table slots */
unsigned Level = Parent? Parent->Level + 1 : 0;
unsigned Slots = ScopeTableSize (Level);
/* Allocate memory */
SymTable* S = xmalloc (sizeof (SymTable) + (Slots-1) * sizeof (SymEntry*));
/* Set variables and clear hash table entries */
S->Left = 0;
S->Right = 0;
S->Childs = 0;
S->Flags = ST_NONE;
S->AddrSize = ADDR_SIZE_DEFAULT;
S->Type = ST_UNDEF;
S->Level = Level;
S->TableSlots = Slots;
S->TableEntries = 0;
S->Parent = Parent;
S->Name = GetStringId (Name);
while (Slots--) {
S->Table[Slots] = 0;
}
/* Insert the symbol table into the child tree of the parent */
if (Parent) {
SymTable* T = Parent->Childs;
if (T == 0) {
/* First entry */
Parent->Childs = S;
} else {
while (1) {
/* Choose next entry */
int Cmp = strcmp (Name, GetString (T->Name));
if (Cmp < 0) {
if (T->Left) {
T = T->Left;
} else {
T->Left = S;
break;
}
} else if (Cmp > 0) {
if (T->Right) {
T = T->Right;
} else {
T->Right = S;
break;
}
} else {
/* Duplicate scope name */
Internal ("Duplicate scope name: `%s'", Name);
}
}
}
}
/* Return the prepared struct */
return S;
}
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void SymEnterLevel (const char* ScopeName, unsigned char Type, unsigned char AddrSize)
/* Enter a new lexical level */
{
/* Map a default address size to something real */
if (AddrSize == ADDR_SIZE_DEFAULT) {
/* Use the segment address size */
AddrSize = GetCurrentSegAddrSize ();
}
/* If we have a current scope, search for the given name and create a
* new one if it doesn't exist. If this is the root scope, just create it.
*/
if (CurrentScope) {
/* Search for the scope, create a new one */
CurrentScope = SymFindScope (CurrentScope, ScopeName, SYM_ALLOC_NEW);
/* Check if the scope has been defined before */
if (CurrentScope->Flags & ST_DEFINED) {
Error ("Duplicate scope `%s'", ScopeName);
}
} else {
CurrentScope = RootScope = NewSymTable (0, ScopeName);
}
/* Mark the scope as defined and set type and address size */
CurrentScope->Flags |= ST_DEFINED;
CurrentScope->AddrSize = AddrSize;
CurrentScope->Type = Type;
}
void SymLeaveLevel (void)
/* Leave the current lexical level */
{
CurrentScope = CurrentScope->Parent;
}
SymTable* SymFindScope (SymTable* Parent, const char* Name, int AllocNew)
/* Find a scope in the given enclosing scope */
{
SymTable** T = &Parent->Childs;
while (*T) {
int Cmp = strcmp (Name, GetString ((*T)->Name));
if (Cmp < 0) {
T = &(*T)->Left;
} else if (Cmp > 0) {
T = &(*T)->Right;
} else {
/* Found the scope */
return *T;
}
}
/* Create a new scope if requested and we didn't find one */
if (*T == 0 && AllocNew) {
*T = NewSymTable (Parent, Name);
}
/* Return the scope */
return *T;
}
SymTable* SymFindAnyScope (SymTable* Parent, const char* Name)
/* Find a scope in the given or any of its parent scopes. The function will
* never create a new symbol, since this can only be done in one specific
* scope.
*/
{
SymTable* Scope;
do {
/* Search in the current table */
Scope = SymFindScope (Parent, Name, SYM_FIND_EXISTING);
if (Scope == 0) {
/* Not found, search in the parent scope, if we have one */
Parent = Parent->Parent;
}
} while (Scope == 0 && Parent != 0);
return Scope;
}
SymEntry* SymFind (SymTable* Scope, const char* Name, int AllocNew)
/* Find a new symbol table entry in the given table. If AllocNew is given and
* the entry is not found, create a new one. Return the entry found, or the
* new entry created, or - in case AllocNew is zero - return 0.
*/
{
SymEntry* S;
int Cmp;
if (IsLocalName (Name)) {
/* Local symbol, get the table */
if (!SymLast) {
/* No last global, so there's no local table */
Error ("No preceeding global symbol");
if (AllocNew) {
return NewSymEntry (Name);
} else {
return 0;
}
}
/* Search for the symbol if we have a table */
Cmp = SymSearchTree (SymLast->Locals, Name, &S);
/* If we found an entry, return it */
if (Cmp == 0) {
return S;
}
if (AllocNew) {
/* Otherwise create a new entry, insert and return it */
SymEntry* N = NewSymEntry (Name);
if (S == 0) {
SymLast->Locals = N;
} else if (Cmp < 0) {
S->Left = N;
} else {
S->Right = N;
}
return N;
}
} else {
/* Global symbol: Get the hash value for the name */
unsigned Hash = HashStr (Name) % Scope->TableSlots;
/* Search for the entry */
Cmp = SymSearchTree (Scope->Table[Hash], Name, &S);
/* If we found an entry, return it */
if (Cmp == 0) {
/* Check for a trampoline entry, in this case return the real
* symbol.
*/
while (S->Flags & SF_TRAMPOLINE) {
S = S->V.Sym;
}
return S;
}
if (AllocNew) {
/* Otherwise create a new entry, insert and return it */
SymEntry* N = NewSymEntry (Name);
if (S == 0) {
Scope->Table[Hash] = N;
} else if (Cmp < 0) {
S->Left = N;
} else {
S->Right = N;
}
N->SymTab = Scope;
++Scope->TableEntries;
return N;
}
}
/* We did not find the entry and AllocNew is false. */
return 0;
}
static SymEntry* SymFindAny (SymTable* Scope, const char* Name)
/* Find a symbol in the given or any of its parent scopes. The function will
* never create a new symbol, since this can only be done in one specific
* scope.
*/
{
SymEntry* Sym;
do {
/* Search in the current table */
Sym = SymFind (Scope, Name, SYM_FIND_EXISTING);
if (Sym) {
/* Found, return it */
return Sym;
} else {
/* Not found, search in the parent scope, if we have one */
Scope = Scope->Parent;
}
} while (Sym == 0 && Scope != 0);
/* Not found */
return 0;
}
int SymIsZP (SymEntry* S)
/* Return true if the symbol is explicitly marked as zeropage symbol */
{
/* Resolve trampoline entries */
if (S->Flags & SF_TRAMPOLINE) {
S = S->V.Sym;
}
/* If the symbol is not a global symbol, was not defined before, check the
* enclosing scope for a symbol with the same name, and return the ZP
* attribute of this symbol if we find one.
*/
if (!IsLocalNameId (S->Name) && (S->Flags & (SF_DEFINED | SF_IMPORT)) == 0 &&
S->SymTab->Parent != 0) {
/* Try to find a symbol with the same name in the enclosing scope */
SymEntry* E = SymFindAny (S->SymTab->Parent, GetString (S->Name));
/* If we found one, use the ZP flag */
if (E && E->AddrSize == ADDR_SIZE_ZP) {
S->AddrSize = ADDR_SIZE_ZP;
}
}
/* Check the ZP flag */
return (S->AddrSize == ADDR_SIZE_ZP);
}
unsigned char GetCurrentSymTabType ()
/* Return the type of the current symbol table */
{
CHECK (CurrentScope != 0);
return CurrentScope->Type;
}
static void SymCheckUndefined (SymEntry* S)
/* Handle an undefined symbol */
{
/* Undefined symbol. It may be...
*
* - An undefined symbol in a nested lexical level. In this
* case, search for the symbol in the higher levels and
* make the entry a trampoline entry if we find one.
*
* - If the symbol is not found, it is a real undefined symbol.
* If the AutoImport flag is set, make it an import. If the
* AutoImport flag is not set, it's an error.
*/
SymEntry* Sym = 0;
if (S->SymTab) {
/* It's a global symbol, get the higher level table */
SymTable* Tab = S->SymTab->Parent;
while (Tab) {
Sym = SymFindAny (Tab, GetString (S->Name));
if (Sym) {
if (Sym->Flags & (SF_DEFINED | SF_IMPORT)) {
/* We've found a symbol in a higher level that is
* either defined in the source, or an import.
*/
break;
} else {
/* The symbol found is undefined itself. Look further */
Tab = Sym->SymTab->Parent;
}
} else {
/* No symbol found */
break;
}
}
}
if (Sym) {
/* We found the symbol in a higher level. Make S a trampoline
* symbol. Beware: We have to transfer the symbol attributes to
* the real symbol and check for any conflicts.
*/
S->Flags |= SF_TRAMPOLINE;
S->V.Sym = Sym;
/* Transfer the flags. Note: S may not be imported, since in that
* case it wouldn't be undefined.
*/
if (S->Flags & SF_EXPORT) {
if (Sym->Flags & SF_IMPORT) {
/* The symbol is already marked as imported external symbol */
PError (&S->Pos, "Symbol `%s' is already an import", GetString (S->Name));
}
Sym->Flags |= (S->Flags & SF_EXPORT);
Sym->ExportSize = S->ExportSize;
}
/* Transfer the referenced flag */
Sym->Flags |= (S->Flags & SF_REFERENCED);
} else {
/* The symbol is definitely undefined */
if (S->Flags & SF_EXPORT) {
/* We will not auto-import an export */
PError (&S->Pos, "Exported symbol `%s' was never defined",
GetString (S->Name));
} else {
if (AutoImport) {
/* Mark as import, will be indexed later */
S->Flags |= SF_IMPORT;
} else {
/* Error */
PError (&S->Pos, "Symbol `%s' is undefined", GetString (S->Name));
}
}
}
}
void SymCheck (void)
/* Run through all symbols and check for anomalies and errors */
{
SymEntry* S;
/* Check for open scopes */
if (CurrentScope->Parent != 0) {
Error ("Local scope was not closed");
}
/* First pass: Walk through all symbols, checking for undefined's and
* changing them to trampoline symbols or make them imports.
*/
S = SymList;
while (S) {
/* If the symbol is marked as global, mark it as export, if it is
* already defined, otherwise mark it as import.
*/
if (S->Flags & SF_GLOBAL) {
S->Flags &= ~SF_GLOBAL;
if (S->Flags & SF_DEFINED) {
S->Flags |= SF_EXPORT;
} else {
S->Flags |= SF_IMPORT;
}
}
/* Handle undefined symbols */
if ((S->Flags & SF_UNDEFMASK) == SF_UNDEFVAL) {
/* This is an undefined symbol. Handle it. */
SymCheckUndefined (S);
}
/* Next symbol */
S = S->List;
}
/* Second pass: Walk again through the symbols. Ignore undefined's, since
* we handled them in the last pass, and ignore trampoline symbols, since
* we handled them in the last pass, too.
*/
S = SymList;
while (S) {
if ((S->Flags & SF_TRAMPOLINE) == 0 &&
(S->Flags & SF_UNDEFMASK) != SF_UNDEFVAL) {
if ((S->Flags & SF_DEFINED) != 0 && (S->Flags & SF_REFERENCED) == 0) {
/* Symbol was defined but never referenced */
PWarning (&S->Pos, 2,
"Symbol `%s' is defined but never used",
GetString (S->Name));
}
if (S->Flags & SF_IMPORT) {
if ((S->Flags & (SF_REFERENCED | SF_FORCED)) == SF_NONE) {
/* Imported symbol is not referenced */
PWarning (&S->Pos, 2,
"Symbol `%s' is imported but never used",
GetString (S->Name));
} else {
/* Give the import an index, count imports */
S->Index = ImportCount++;
S->Flags |= SF_INDEXED;
}
}
if (S->Flags & SF_EXPORT) {
/* Give the export an index, count exports */
S->Index = ExportCount++;
S->Flags |= SF_INDEXED;
}
}
/* Next symbol */
S = S->List;
}
}
void SymDump (FILE* F)
/* Dump the symbol table */
{
SymEntry* S = SymList;
while (S) {
/* Ignore trampoline symbols */
if ((S->Flags & SF_TRAMPOLINE) != 0) {
fprintf (F,
"%-24s %s %s %s %s %s\n",
GetString (S->Name),
(S->Flags & SF_DEFINED)? "DEF" : "---",
(S->Flags & SF_REFERENCED)? "REF" : "---",
(S->Flags & SF_IMPORT)? "IMP" : "---",
(S->Flags & SF_EXPORT)? "EXP" : "---",
AddrSizeToStr (S->AddrSize));
}
/* Next symbol */
S = S->List;
}
}
void WriteImports (void)
/* Write the imports list to the object file */
{
SymEntry* S;
/* Tell the object file module that we're about to start the imports */
ObjStartImports ();
/* Write the import count to the list */
ObjWriteVar (ImportCount);
/* Walk throught list and write all valid imports to the file. An import
* is considered valid, if it is either referenced, or the forced bit is
* set. Otherwise, the import is ignored (no need to link in something
* that isn't used).
*/
S = SymList;
while (S) {
if ((S->Flags & (SF_TRAMPOLINE | SF_IMPORT)) == SF_IMPORT &&
(S->Flags & (SF_REFERENCED | SF_FORCED)) != 0) {
if (S->AddrSize == ADDR_SIZE_ZP) {
ObjWrite8 (IMP_ZP);
} else {
ObjWrite8 (IMP_ABS);
}
ObjWriteVar (S->Name);
ObjWritePos (&S->Pos);
}
S = S->List;
}
/* Done writing imports */
ObjEndImports ();
}
void WriteExports (void)
/* Write the exports list to the object file */
{
SymEntry* S;
unsigned Type;
/* Tell the object file module that we're about to start the exports */
ObjStartExports ();
/* Write the export count to the list */
ObjWriteVar (ExportCount);
/* Walk throught list and write all exports to the file */
S = SymList;
while (S) {
if ((S->Flags & SF_EXPMASK) == SF_EXPVAL) {
long ConstVal;
/* Get the expression bits */
unsigned char ExprMask = SymIsConst (S, &ConstVal)? EXP_CONST : EXP_EXPR;
ExprMask |= (S->ExportSize == ADDR_SIZE_ZP)? EXP_ZP : EXP_ABS;
ExprMask |= (S->Flags & SF_LABEL)? EXP_LABEL : EXP_EQUATE;
/* Count the number of ConDes types */
for (Type = 0; Type < CD_TYPE_COUNT; ++Type) {
if (S->ConDesPrio[Type] != CD_PRIO_NONE) {
INC_EXP_CONDES_COUNT (ExprMask);
}
}
/* Write the type */
ObjWrite8 (ExprMask);
/* Write any ConDes declarations */
if (GET_EXP_CONDES_COUNT (ExprMask) > 0) {
for (Type = 0; Type < CD_TYPE_COUNT; ++Type) {
unsigned char Prio = S->ConDesPrio[Type];
if (Prio != CD_PRIO_NONE) {
ObjWrite8 (CD_BUILD (Type, Prio));
}
}
}
/* Write the name */
ObjWriteVar (S->Name);
/* Write the value */
if ((ExprMask & EXP_MASK_VAL) == EXP_CONST) {
/* Constant value */
ObjWrite32 (ConstVal);
} else {
/* Expression involved */
WriteExpr (S->V.Expr);
}
/* Write the source file position */
ObjWritePos (&S->Pos);
}
S = S->List;
}
/* Done writing exports */
ObjEndExports ();
}
void WriteDbgSyms (void)
/* Write a list of all symbols to the object file */
{
unsigned Count;
SymEntry* S;
/* Tell the object file module that we're about to start the debug info */
ObjStartDbgSyms ();
/* Check if debug info is requested */
if (DbgSyms) {
/* Walk through the list and count the symbols */
Count = 0;
S = SymList;
while (S) {
if ((S->Flags & SF_DBGINFOMASK) == SF_DBGINFOVAL) {
++Count;
}
S = S->List;
}
/* Write the symbol count to the list */
ObjWriteVar (Count);
/* Walk through list and write all symbols to the file */
S = SymList;
while (S) {
if ((S->Flags & SF_DBGINFOMASK) == SF_DBGINFOVAL) {
long ConstVal;
/* Get the expression bits */
unsigned char ExprMask = (SymIsConst (S, &ConstVal))? EXP_CONST : EXP_EXPR;
ExprMask |= (S->AddrSize == ADDR_SIZE_ZP)? EXP_ZP : EXP_ABS;
ExprMask |= (S->Flags & SF_LABEL)? EXP_LABEL : EXP_EQUATE;
/* Write the type */
ObjWrite8 (ExprMask);
/* Write the name */
ObjWriteVar (S->Name);
/* Write the value */
if ((ExprMask & EXP_MASK_VAL) == EXP_CONST) {
/* Constant value */
ObjWrite32 (ConstVal);
} else {
/* Expression involved */
WriteExpr (S->V.Expr);
}
/* Write the source file position */
ObjWritePos (&S->Pos);
}
S = S->List;
}
} else {
/* No debug symbols */
ObjWriteVar (0);
}
/* Done writing debug symbols */
ObjEndDbgSyms ();
}