mirror of
https://github.com/cc65/cc65.git
synced 2024-12-27 00:29:31 +00:00
a07e05beb8
git-svn-id: svn://svn.cc65.org/cc65/trunk@1203 b7a2c559-68d2-44c3-8de9-860c34a00d81
1233 lines
31 KiB
C
1233 lines
31 KiB
C
/*****************************************************************************/
|
|
/* */
|
|
/* symtab.c */
|
|
/* */
|
|
/* Symbol table for the ca65 macroassembler */
|
|
/* */
|
|
/* */
|
|
/* */
|
|
/* (C) 1998-2000 Ullrich von Bassewitz */
|
|
/* Wacholderweg 14 */
|
|
/* D-70597 Stuttgart */
|
|
/* EMail: uz@musoftware.de */
|
|
/* */
|
|
/* */
|
|
/* 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 "cddefs.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 "symtab.h"
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Data */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
/* Bits for the Flags value in SymEntry */
|
|
#define SF_USER 0x0001 /* User bit */
|
|
#define SF_TRAMPOLINE 0x0002 /* Trampoline entry */
|
|
#define SF_EXPORT 0x0004 /* Export this symbol */
|
|
#define SF_IMPORT 0x0008 /* Import this symbol */
|
|
#define SF_GLOBAL 0x0010 /* Global symbol */
|
|
#define SF_ZP 0x0020 /* Declared as zeropage symbol */
|
|
#define SF_ABS 0x0040 /* Declared as absolute symbol */
|
|
#define SF_LABEL 0x0080 /* Used as a label */
|
|
#define SF_INDEXED 0x0800 /* Index is valid */
|
|
#define SF_CONST 0x1000 /* The symbol has a constant value */
|
|
#define SF_MULTDEF 0x2000 /* Multiply defined symbol */
|
|
#define SF_DEFINED 0x4000 /* Defined */
|
|
#define SF_REFERENCED 0x8000 /* Referenced */
|
|
|
|
/* Combined stuff */
|
|
#define SF_UNDEFMASK (SF_REFERENCED | SF_DEFINED | SF_IMPORT)
|
|
#define SF_UNDEFVAL (SF_REFERENCED)
|
|
#define SF_IMPMASK (SF_TRAMPOLINE | SF_IMPORT | SF_REFERENCED)
|
|
#define SF_IMPVAL (SF_IMPORT | 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)
|
|
|
|
/* Structure of a symbol table entry */
|
|
struct SymEntry {
|
|
SymEntry* Left; /* Lexically smaller entry */
|
|
SymEntry* Right; /* Lexically larger entry */
|
|
SymEntry* List; /* List of all entries */
|
|
SymEntry* Locals; /* Root of subtree for local symbols */
|
|
struct SymTable* SymTab; /* Table this symbol is in, 0 for locals */
|
|
FilePos Pos; /* File position for this symbol */
|
|
unsigned Flags; /* Symbol flags */
|
|
unsigned Index; /* Index of import/export entries */
|
|
union {
|
|
struct ExprNode* Expr; /* Expression if CONST not set */
|
|
long Val; /* Value (if CONST set) */
|
|
SymEntry* Sym; /* Symbol (if trampoline entry) */
|
|
} V;
|
|
unsigned char ConDesPrio[CD_TYPE_COUNT]; /* ConDes priorities... */
|
|
/* ...actually value+1 (used as flag) */
|
|
char Name [1]; /* Dynamic allocation */
|
|
};
|
|
|
|
|
|
|
|
/* Definitions for the hash table */
|
|
#define MAIN_HASHTAB_SIZE 213
|
|
#define SUB_HASHTAB_SIZE 53
|
|
typedef struct SymTable SymTable;
|
|
struct SymTable {
|
|
unsigned TableSlots; /* Number of hash table slots */
|
|
unsigned TableEntries; /* Number of entries in the table */
|
|
SymTable* BackLink; /* Link to enclosing scope if any */
|
|
SymEntry* Table [1]; /* Dynamic allocation */
|
|
};
|
|
|
|
|
|
|
|
/* Arguments for SymFind */
|
|
#define SF_FIND_EXISTING 0
|
|
#define SF_ALLOC_NEW 1
|
|
|
|
|
|
|
|
/* Symbol table variables */
|
|
static SymEntry* SymList = 0; /* List of all symbol table entries */
|
|
static SymEntry* SymLast = 0; /* Pointer to last defined symbol */
|
|
static SymTable* SymTab = 0; /* Pointer to current symbol table */
|
|
static SymTable* RootTab = 0; /* Root symbol table */
|
|
static unsigned ImportCount = 0;/* Counter for import symbols */
|
|
static unsigned ExportCount = 0;/* Counter for export symbols */
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Internally used functions */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static int IsLocal (const char* Name)
|
|
/* Return true if Name is the name of a local symbol */
|
|
{
|
|
return (*Name == LocalStart);
|
|
}
|
|
|
|
|
|
|
|
static SymEntry* NewSymEntry (const char* Name)
|
|
/* Allocate a symbol table entry, initialize and return it */
|
|
{
|
|
SymEntry* S;
|
|
unsigned Len;
|
|
|
|
/* Get the length of the name */
|
|
Len = strlen (Name);
|
|
|
|
/* Allocate memory */
|
|
S = xmalloc (sizeof (SymEntry) + Len);
|
|
|
|
/* Initialize the entry */
|
|
S->Left = 0;
|
|
S->Right = 0;
|
|
S->Locals = 0;
|
|
S->SymTab = 0;
|
|
S->Pos = CurPos;
|
|
S->Flags = 0;
|
|
S->V.Expr = 0;
|
|
memset (S->ConDesPrio, 0, sizeof (S->ConDesPrio));
|
|
memcpy (S->Name, Name, Len+1);
|
|
|
|
/* Insert it into the list of all entries */
|
|
S->List = SymList;
|
|
SymList = S;
|
|
|
|
/* Return the initialized entry */
|
|
return S;
|
|
}
|
|
|
|
|
|
|
|
static SymTable* NewSymTable (unsigned Size)
|
|
/* Allocate a symbol table on the heap and return it */
|
|
{
|
|
SymTable* S;
|
|
|
|
/* Allocate memory */
|
|
S = xmalloc (sizeof (SymTable) + (Size-1) * sizeof (SymEntry*));
|
|
|
|
/* Set variables and clear hash table entries */
|
|
S->TableSlots = Size;
|
|
S->TableEntries = 0;
|
|
S->BackLink = 0;
|
|
while (Size--) {
|
|
S->Table [Size] = 0;
|
|
}
|
|
|
|
/* Return the prepared struct */
|
|
return S;
|
|
}
|
|
|
|
|
|
|
|
static int SearchSymTab (SymEntry* T, const char* Name, SymEntry** E)
|
|
/* Search in the given table for a name (Hash is the hash value of Name and
|
|
* is given as parameter so that it will not get calculated twice if we search
|
|
* in more than one table). 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.
|
|
*/
|
|
{
|
|
int Cmp;
|
|
|
|
/* Is there a tree? */
|
|
if (T == 0) {
|
|
*E = 0;
|
|
return 1;
|
|
}
|
|
|
|
/* We have a table, search it */
|
|
while (1) {
|
|
/* Choose next entry */
|
|
Cmp = strcmp (Name, T->Name);
|
|
if (Cmp < 0 && T->Left) {
|
|
T = T->Left;
|
|
} else if (Cmp > 0 && T->Right) {
|
|
T = T->Right;
|
|
} else {
|
|
/* Found or end of search */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return the search result */
|
|
*E = T;
|
|
return Cmp;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Code */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static SymEntry* SymFind (SymTable* Tab, 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;
|
|
unsigned Hash;
|
|
|
|
if (IsLocal (Name)) {
|
|
|
|
/* Local symbol, get the table */
|
|
if (!SymLast) {
|
|
/* No last global, so there's no local table */
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
if (AllocNew) {
|
|
return NewSymEntry (Name);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Search for the symbol if we have a table */
|
|
Cmp = SearchSymTab (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 */
|
|
Hash = HashStr (Name) % Tab->TableSlots;
|
|
|
|
/* Search for the entry */
|
|
Cmp = SearchSymTab (Tab->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.
|
|
*/
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
return S->V.Sym;
|
|
} else {
|
|
return S;
|
|
}
|
|
}
|
|
|
|
if (AllocNew) {
|
|
|
|
/* Otherwise create a new entry, insert and return it */
|
|
SymEntry* N = NewSymEntry (Name);
|
|
if (S == 0) {
|
|
Tab->Table [Hash] = N;
|
|
} else if (Cmp < 0) {
|
|
S->Left = N;
|
|
} else {
|
|
S->Right = N;
|
|
}
|
|
N->SymTab = Tab;
|
|
++Tab->TableEntries;
|
|
return N;
|
|
|
|
}
|
|
}
|
|
|
|
/* We did not find the entry and AllocNew is false. */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static SymEntry* SymFindAny (SymTable* Tab, const char* Name)
|
|
/* Find a symbol in any table */
|
|
{
|
|
SymEntry* Sym;
|
|
do {
|
|
/* Search in the current table */
|
|
Sym = SymFind (Tab, Name, 0);
|
|
if (Sym) {
|
|
/* Found, return it */
|
|
return Sym;
|
|
} else {
|
|
/* Not found, search in the backlink, if we have one */
|
|
Tab = Tab->BackLink;
|
|
}
|
|
} while (Sym == 0 && Tab != 0);
|
|
|
|
/* Not found */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static SymEntry* SymRefInternal (SymTable* Table, const char* Name)
|
|
/* Search for the symbol in the given table and return it */
|
|
{
|
|
/* Try to find the symbol, create a new one if the symbol does not exist */
|
|
SymEntry* S = SymFind (Table, Name, SF_ALLOC_NEW);
|
|
|
|
/* Mark the symbol as referenced */
|
|
S->Flags |= SF_REFERENCED;
|
|
|
|
/* Return it */
|
|
return S;
|
|
}
|
|
|
|
|
|
|
|
void SymEnterLevel (void)
|
|
/* Enter a new lexical level */
|
|
{
|
|
if (RootTab == 0) {
|
|
/* Create the main symbol table */
|
|
RootTab = SymTab = NewSymTable (MAIN_HASHTAB_SIZE);
|
|
} else {
|
|
/* Create a local symbol table */
|
|
SymTable* LocalSyms;
|
|
LocalSyms = NewSymTable (SUB_HASHTAB_SIZE);
|
|
LocalSyms->BackLink = SymTab;
|
|
SymTab = LocalSyms;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymLeaveLevel (void)
|
|
/* Leave the current lexical level */
|
|
{
|
|
SymTab = SymTab->BackLink;
|
|
}
|
|
|
|
|
|
|
|
void SymDef (const char* Name, ExprNode* Expr, int ZP, int Label)
|
|
/* Define a new symbol */
|
|
{
|
|
/* Do we have such a symbol? */
|
|
SymEntry* S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
if (S->Flags & SF_IMPORT) {
|
|
/* Defined symbol is marked as imported external symbol */
|
|
Error (ERR_SYM_ALREADY_IMPORT, Name);
|
|
return;
|
|
}
|
|
if (S->Flags & SF_DEFINED) {
|
|
/* Multiple definition */
|
|
Error (ERR_SYM_ALREADY_DEFINED, Name);
|
|
S->Flags |= SF_MULTDEF;
|
|
return;
|
|
}
|
|
|
|
/* Set the symbol data */
|
|
if (IsConstExpr (Expr)) {
|
|
/* Expression is const, store the value */
|
|
S->Flags |= SF_CONST;
|
|
S->V.Val = GetExprVal (Expr);
|
|
FreeExpr (Expr);
|
|
} else {
|
|
/* Not const, store the expression */
|
|
S->V.Expr = Expr;
|
|
}
|
|
S->Flags |= SF_DEFINED;
|
|
if (ZP) {
|
|
S->Flags |= SF_ZP;
|
|
}
|
|
if (Label) {
|
|
S->Flags |= SF_LABEL;
|
|
}
|
|
|
|
/* If the symbol is a ZP symbol, check if the value is in correct range */
|
|
if (S->Flags & SF_ZP) {
|
|
/* Already marked as ZP symbol by some means */
|
|
if (!IsByteExpr (Expr)) {
|
|
Error (ERR_RANGE);
|
|
}
|
|
}
|
|
|
|
/* If this is not a local symbol, remember it as the last global one */
|
|
if (!IsLocal (Name)) {
|
|
SymLast = S;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
SymEntry* SymRef (const char* Name)
|
|
/* Search for the symbol and return it */
|
|
{
|
|
/* Reference the symbol in the current table */
|
|
return SymRefInternal (SymTab, Name);
|
|
}
|
|
|
|
|
|
|
|
SymEntry* SymRefGlobal (const char* Name)
|
|
/* Search for the symbol in the global namespace and return it */
|
|
{
|
|
/* Reference the symbol in the current table */
|
|
return SymRefInternal (RootTab, Name);
|
|
}
|
|
|
|
|
|
|
|
void SymImport (const char* Name, int ZP)
|
|
/* Mark the given symbol as an imported symbol */
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Don't accept local symbols */
|
|
if (IsLocal (Name)) {
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
return;
|
|
}
|
|
|
|
/* Do we have such a symbol? */
|
|
S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
if (S->Flags & SF_DEFINED) {
|
|
Error (ERR_SYM_ALREADY_DEFINED, Name);
|
|
S->Flags |= SF_MULTDEF;
|
|
return;
|
|
}
|
|
if (S->Flags & SF_EXPORT) {
|
|
/* The symbol is already marked as exported symbol */
|
|
Error (ERR_SYM_ALREADY_EXPORT, Name);
|
|
return;
|
|
}
|
|
|
|
/* If the symbol is marked as global, check the symbol size, then do
|
|
* silently remove the global flag
|
|
*/
|
|
if (S->Flags & SF_GLOBAL) {
|
|
if ((ZP != 0) != ((S->Flags & SF_ZP) != 0)) {
|
|
Error (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
S->Flags &= ~SF_GLOBAL;
|
|
}
|
|
|
|
/* Set the symbol data */
|
|
S->Flags |= SF_IMPORT;
|
|
if (ZP) {
|
|
S->Flags |= SF_ZP;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymExport (const char* Name, int ZP)
|
|
/* Mark the given symbol as an exported symbol */
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Don't accept local symbols */
|
|
if (IsLocal (Name)) {
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
return;
|
|
}
|
|
|
|
/* Do we have such a symbol? */
|
|
S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
if (S->Flags & SF_IMPORT) {
|
|
/* The symbol is already marked as imported external symbol */
|
|
Error (ERR_SYM_ALREADY_IMPORT, Name);
|
|
return;
|
|
}
|
|
|
|
/* If the symbol is marked as global, check the symbol size, then do
|
|
* silently remove the global flag
|
|
*/
|
|
if (S->Flags & SF_GLOBAL) {
|
|
if ((ZP != 0) != ((S->Flags & SF_ZP) != 0)) {
|
|
Error (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
S->Flags &= ~SF_GLOBAL;
|
|
}
|
|
|
|
/* Set the symbol data */
|
|
S->Flags |= SF_EXPORT | SF_REFERENCED;
|
|
if (ZP) {
|
|
S->Flags |= SF_ZP;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymGlobal (const char* Name, int ZP)
|
|
/* Mark the given symbol as a global symbol, that is, as a symbol that is
|
|
* either imported or exported.
|
|
*/
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Don't accept local symbols */
|
|
if (IsLocal (Name)) {
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
return;
|
|
}
|
|
|
|
/* Search for this symbol, create a new entry if needed */
|
|
S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
|
|
/* If the symbol is already marked as import or export, check the
|
|
* size of the definition, then bail out. */
|
|
if (S->Flags & SF_IMPORT || S->Flags & SF_EXPORT) {
|
|
if ((ZP != 0) != ((S->Flags & SF_ZP) != 0)) {
|
|
Error (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Mark the symbol */
|
|
S->Flags |= SF_GLOBAL;
|
|
if (ZP) {
|
|
S->Flags |= SF_ZP;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymConDes (const char* Name, 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.
|
|
*/
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* 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);
|
|
|
|
/* Don't accept local symbols */
|
|
if (IsLocal (Name)) {
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
return;
|
|
}
|
|
|
|
/* Do we have such a symbol? */
|
|
S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
if (S->Flags & SF_IMPORT) {
|
|
/* The symbol is already marked as imported external symbol */
|
|
Error (ERR_SYM_ALREADY_IMPORT, Name);
|
|
return;
|
|
}
|
|
|
|
/* If the symbol is marked as global, silently remove the global flag */
|
|
if (S->Flags & SF_GLOBAL) {
|
|
S->Flags &= ~SF_GLOBAL;
|
|
}
|
|
|
|
/* Check if the symbol was not already defined as ZP symbol */
|
|
if ((S->Flags & SF_ZP) != 0) {
|
|
Error (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
|
|
/* If the symbol was already declared as a condes, 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 (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
}
|
|
S->ConDesPrio[Type] = Prio;
|
|
|
|
/* Set the symbol data */
|
|
S->Flags |= SF_EXPORT | SF_REFERENCED;
|
|
}
|
|
|
|
|
|
|
|
int SymIsDef (const char* Name)
|
|
/* Return true if the given symbol is already defined */
|
|
{
|
|
SymEntry* S = SymFindAny (SymTab, Name);
|
|
return S != 0 && (S->Flags & SF_DEFINED) != 0;
|
|
}
|
|
|
|
|
|
|
|
int SymIsRef (const char* Name)
|
|
/* Return true if the given symbol has been referenced */
|
|
{
|
|
SymEntry* S = SymFindAny (SymTab, Name);
|
|
return S != 0 && (S->Flags & SF_REFERENCED) != 0;
|
|
}
|
|
|
|
|
|
|
|
int SymIsConst (SymEntry* S)
|
|
/* Return true if the given symbol has a constant value */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check for constness */
|
|
if (S->Flags & SF_CONST) {
|
|
return 1;
|
|
} else if ((S->Flags & SF_DEFINED) && IsConstExpr (S->V.Expr)) {
|
|
/* Constant expression, remember the value */
|
|
ExprNode* Expr = S->V.Expr;
|
|
S->Flags |= SF_CONST;
|
|
S->V.Val = GetExprVal (Expr);
|
|
FreeExpr (Expr);
|
|
return 1;
|
|
}
|
|
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 (!IsLocal (S->Name) &&
|
|
(S->Flags & (SF_ZP | SF_ABS | SF_DEFINED | SF_IMPORT)) == 0 &&
|
|
S->SymTab->BackLink != 0) {
|
|
|
|
/* Try to find a symbol with the same name in the enclosing scope */
|
|
SymEntry* E = SymFindAny (S->SymTab->BackLink, S->Name);
|
|
|
|
/* If we found one, use the ZP flag */
|
|
if (E && (E->Flags & SF_ZP) != 0) {
|
|
S->Flags |= SF_ZP;
|
|
}
|
|
}
|
|
|
|
/* Check the ZP flag */
|
|
return (S->Flags & SF_ZP) != 0;
|
|
}
|
|
|
|
|
|
|
|
int SymIsImport (SymEntry* S)
|
|
/* Return true if the given symbol is marked as import */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check the import flag */
|
|
return (S->Flags & SF_IMPORT) != 0;
|
|
}
|
|
|
|
|
|
|
|
int SymHasExpr (SymEntry* S)
|
|
/* Return true if the given symbol has an associated expression */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check the expression */
|
|
return ((S->Flags & SF_DEFINED) != 0 &&
|
|
(S->Flags & SF_IMPORT) == 0 &&
|
|
(S->Flags & SF_CONST) == 0);
|
|
}
|
|
|
|
|
|
|
|
void SymFinalize (SymEntry* S)
|
|
/* Finalize a symbol expression if there is one */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check if we have an expression */
|
|
if (SymHasExpr (S)) {
|
|
S->V.Expr = FinalizeExpr (S->V.Expr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymMarkUser (SymEntry* S)
|
|
/* Set a user mark on the specified symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Set the bit */
|
|
S->Flags |= SF_USER;
|
|
}
|
|
|
|
|
|
|
|
void SymUnmarkUser (SymEntry* S)
|
|
/* Remove a user mark from the specified symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Reset the bit */
|
|
S->Flags &= ~SF_USER;
|
|
}
|
|
|
|
|
|
|
|
int SymHasUserMark (SymEntry* S)
|
|
/* Return the state of the user mark for the specified symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check the bit */
|
|
return (S->Flags & SF_USER) != 0;
|
|
}
|
|
|
|
|
|
|
|
long GetSymVal (SymEntry* S)
|
|
/* Return the symbol value */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
PRECONDITION ((S->Flags & SF_DEFINED) != 0 && (S->Flags & SF_CONST) != 0);
|
|
return S->V.Val;
|
|
}
|
|
|
|
|
|
|
|
ExprNode* GetSymExpr (SymEntry* S)
|
|
/* Get the expression for a non-const symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
PRECONDITION (S != 0 && (S->Flags & SF_CONST) == 0);
|
|
return S->V.Expr;
|
|
}
|
|
|
|
|
|
|
|
const char* GetSymName (SymEntry* S)
|
|
/* Return the name of the symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
return S->Name;
|
|
}
|
|
|
|
|
|
|
|
unsigned GetSymIndex (SymEntry* S)
|
|
/* Return the symbol index for the given symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
PRECONDITION (S != 0 && (S->Flags & SF_INDEXED));
|
|
return S->Index;
|
|
}
|
|
|
|
|
|
|
|
const FilePos* GetSymPos (SymEntry* S)
|
|
/* Return the position of first occurence in the source for the given symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
PRECONDITION (S != 0);
|
|
return &S->Pos;
|
|
}
|
|
|
|
|
|
|
|
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->BackLink;
|
|
while (Tab) {
|
|
Sym = SymFindAny (Tab, 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->BackLink;
|
|
}
|
|
} 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, ERR_SYM_ALREADY_IMPORT, S->Name);
|
|
}
|
|
Sym->Flags |= S->Flags & (SF_EXPORT | SF_ZP);
|
|
}
|
|
|
|
/* 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, ERR_EXPORT_UNDEFINED, S->Name);
|
|
} else {
|
|
if (AutoImport) {
|
|
/* Mark as import, will be indexed later */
|
|
S->Flags |= SF_IMPORT;
|
|
} else {
|
|
/* Error */
|
|
PError (&S->Pos, ERR_SYM_UNDEFINED, S->Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymCheck (void)
|
|
/* Run through all symbols and check for anomalies and errors */
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Check for open lexical levels */
|
|
if (SymTab->BackLink != 0) {
|
|
Error (ERR_OPEN_PROC);
|
|
}
|
|
|
|
/* 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, WARN_SYM_NOT_REFERENCED, S->Name);
|
|
}
|
|
if (S->Flags & SF_IMPORT) {
|
|
if ((S->Flags & SF_REFERENCED) == 0) {
|
|
/* Imported symbol is not referenced */
|
|
PWarning (&S->Pos, WARN_IMPORT_NOT_REFERENCED, 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",
|
|
S->Name,
|
|
(S->Flags & SF_DEFINED)? "DEF" : "---",
|
|
(S->Flags & SF_REFERENCED)? "REF" : "---",
|
|
(S->Flags & SF_IMPORT)? "IMP" : "---",
|
|
(S->Flags & SF_EXPORT)? "EXP" : "---",
|
|
(S->Flags & SF_ZP)? "ZP" : "--");
|
|
}
|
|
/* 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 imports to the file */
|
|
S = SymList;
|
|
while (S) {
|
|
if ((S->Flags & SF_IMPMASK) == SF_IMPVAL) {
|
|
if (S->Flags & SF_ZP) {
|
|
ObjWrite8 (IMP_ZP);
|
|
} else {
|
|
ObjWrite8 (IMP_ABS);
|
|
}
|
|
ObjWriteStr (S->Name);
|
|
ObjWritePos (&S->Pos);
|
|
}
|
|
S = S->List;
|
|
}
|
|
|
|
/* Done writing imports */
|
|
ObjEndImports ();
|
|
}
|
|
|
|
|
|
|
|
static unsigned char GetExprMask (SymEntry* S)
|
|
/* Return the expression bits for the given symbol table entry */
|
|
{
|
|
unsigned char ExprMask;
|
|
|
|
/* Check if the symbol is const */
|
|
ExprMask = (SymIsConst (S))? EXP_CONST : EXP_EXPR;
|
|
|
|
/* Add zeropage/abs bits */
|
|
ExprMask |= (S->Flags & SF_ZP)? EXP_ZP : EXP_ABS;
|
|
|
|
/* Add the label/equate bits */
|
|
ExprMask |= (S->Flags & SF_LABEL)? EXP_LABEL : EXP_EQUATE;
|
|
|
|
/* Return the mask */
|
|
return ExprMask;
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
unsigned char ExprMask;
|
|
|
|
/* Finalize an associated expression if we have one */
|
|
SymFinalize (S);
|
|
|
|
/* Get the expression bits */
|
|
ExprMask = GetExprMask (S);
|
|
|
|
/* 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 */
|
|
ObjWriteStr (S->Name);
|
|
|
|
/* Write the value */
|
|
if ((ExprMask & EXP_MASK_VAL) == EXP_CONST) {
|
|
/* Constant value */
|
|
ObjWrite32 (S->V.Val);
|
|
} 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) {
|
|
unsigned char ExprMask;
|
|
|
|
/* Finalize an associated expression if we have one */
|
|
SymFinalize (S);
|
|
|
|
/* Get the expression bits */
|
|
ExprMask = GetExprMask (S);
|
|
|
|
/* Write the type */
|
|
ObjWrite8 (ExprMask);
|
|
|
|
/* Write the name */
|
|
ObjWriteStr (S->Name);
|
|
|
|
/* Write the value */
|
|
if ((ExprMask & EXP_MASK_VAL) == EXP_CONST) {
|
|
/* Constant value */
|
|
ObjWrite32 (S->V.Val);
|
|
} 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 ();
|
|
}
|
|
|
|
|
|
|