/*****************************************************************************/ /* */ /* symtab.c */ /* */ /* Symbol table for the ca65 macroassembler */ /* */ /* */ /* */ /* (C) 1998-2014, 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 "check.h" #include "hashfunc.h" #include "mmodel.h" #include "scopedefs.h" #include "symdefs.h" #include "xmalloc.h" /* ca65 */ #include "dbginfo.h" #include "error.h" #include "expr.h" #include "global.h" #include "objfile.h" #include "scanner.h" #include "segment.h" #include "sizeof.h" #include "span.h" #include "spool.h" #include "studyexpr.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) /* Symbol tables */ SymTable* CurrentScope = 0; /* Pointer to current symbol table */ SymTable* RootScope = 0; /* Root symbol table */ static SymTable* LastScope = 0; /* Pointer to last scope in list */ static unsigned ScopeCount = 0; /* Number of scopes */ /* Symbol table variables */ static unsigned ImportCount = 0; /* Counter for import symbols */ static unsigned ExportCount = 0; /* Counter for export symbols */ /*****************************************************************************/ /* Internally used functions */ /*****************************************************************************/ static int IsDbgSym (const SymEntry* S) /* Return true if this is a debug symbol */ { if ((S->Flags & (SF_DEFINED | SF_UNUSED)) == SF_DEFINED) { /* Defined symbols are debug symbols if they aren't sizes */ return !IsSizeOfSymbol (S); } else { /* Others are debug symbols if they're referenced imports */ return ((S->Flags & SF_REFIMP) == SF_REFIMP); } } 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 StrBuf* 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->Next = 0; S->Left = 0; S->Right = 0; S->Childs = 0; S->Label = 0; S->Spans = AUTO_COLLECTION_INITIALIZER; S->Id = ScopeCount++; S->Flags = ST_NONE; S->AddrSize = ADDR_SIZE_DEFAULT; S->Type = SCOPE_UNDEF; S->Level = Level; S->TableSlots = Slots; S->TableEntries = 0; S->Parent = Parent; S->Name = GetStrBufId (Name); while (Slots--) { S->Table[Slots] = 0; } /* Insert the symbol table into the list of all symbol tables */ if (RootScope == 0) { RootScope = S; } else { LastScope->Next = S; } LastScope = S; /* 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 = SB_Compare (Name, GetStrBuf (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: `%m%p'", Name); } } } } /* Return the prepared struct */ return S; } /*****************************************************************************/ /* Code */ /*****************************************************************************/ void SymEnterLevel (const StrBuf* ScopeName, unsigned char Type, unsigned char AddrSize, SymEntry* ScopeLabel) /* 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 `%m%p'", ScopeName); } } else { CurrentScope = RootScope = NewSymTable (0, ScopeName); } /* Mark the scope as defined and set type, address size and owner symbol */ CurrentScope->Flags |= ST_DEFINED; CurrentScope->AddrSize = AddrSize; CurrentScope->Type = Type; CurrentScope->Label = ScopeLabel; /* If this is a scope that allows to emit data into segments, add spans ** for all currently existing segments. Doing this for just a few scope ** types is not really necessary but an optimization, because it does not ** allocate memory for useless data (unhandled types here don't occupy ** space in any segment). */ if (CurrentScope->Type <= SCOPE_HAS_DATA) { OpenSpanList (&CurrentScope->Spans); } } void SymLeaveLevel (void) /* Leave the current lexical level */ { /* If this is a scope that allows to emit data into segments, close the ** open the spans. */ if (CurrentScope->Type <= SCOPE_HAS_DATA) { CloseSpanList (&CurrentScope->Spans); } /* If we have spans, the first one is the segment that was active, when the ** scope was opened. Set the size of the scope to the number of data bytes ** emitted into this segment. If we have an owner symbol set the size of ** this symbol, too. */ if (CollCount (&CurrentScope->Spans) > 0) { const Span* S = CollAtUnchecked (&CurrentScope->Spans, 0); unsigned long Size = GetSpanSize (S); DefSizeOfScope (CurrentScope, Size); if (CurrentScope->Label) { DefSizeOfSymbol (CurrentScope->Label, Size); } } /* Mark the scope as closed */ CurrentScope->Flags |= ST_CLOSED; /* Leave the scope */ CurrentScope = CurrentScope->Parent; } SymTable* SymFindScope (SymTable* Parent, const StrBuf* Name, SymFindAction Action) /* Find a scope in the given enclosing scope */ { SymTable** T = &Parent->Childs; while (*T) { int Cmp = SB_Compare (Name, GetStrBuf ((*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 && (Action & SYM_ALLOC_NEW) != 0) { *T = NewSymTable (Parent, Name); } /* Return the scope */ return *T; } SymTable* SymFindAnyScope (SymTable* Parent, const StrBuf* 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* SymFindLocal (SymEntry* Parent, const StrBuf* Name, SymFindAction Action) /* Find a cheap local symbol. If Action contains SYM_ALLOC_NEW and the entry is ** not found, create a new one. Return the entry found, or the new entry ** created, or - in case Action is SYM_FIND_EXISTING - return 0. */ { SymEntry* S; int Cmp; /* Local symbol, get the table */ if (!Parent) { /* No last global, so there's no local table */ Error ("No preceeding global symbol"); if (Action & SYM_ALLOC_NEW) { return NewSymEntry (Name, SF_LOCAL); } else { return 0; } } /* Search for the symbol if we have a table */ Cmp = SymSearchTree (Parent->Locals, Name, &S); /* If we found an entry, return it */ if (Cmp == 0) { return S; } if (Action & SYM_ALLOC_NEW) { /* Otherwise create a new entry, insert and return it */ SymEntry* N = NewSymEntry (Name, SF_LOCAL); N->Sym.Entry = Parent; if (S == 0) { Parent->Locals = N; } else if (Cmp < 0) { S->Left = N; } else { S->Right = N; } return N; } /* We did not find the entry and AllocNew is false. */ return 0; } SymEntry* SymFind (SymTable* Scope, const StrBuf* Name, SymFindAction Action) /* Find a new symbol table entry in the given table. If Action contains ** SYM_ALLOC_NEW and the entry is not found, create a new one. Return the ** entry found, or the new entry created, or - in case Action is ** SYM_FIND_EXISTING - return 0. */ { SymEntry* S; /* Global symbol: Get the hash value for the name */ unsigned Hash = HashBuf (Name) % Scope->TableSlots; /* Search for the entry */ int Cmp = SymSearchTree (Scope->Table[Hash], Name, &S); /* If we found an entry, return it */ if (Cmp == 0) { if ((Action & SYM_CHECK_ONLY) == 0 && SymTabIsClosed (Scope)) { S->Flags |= SF_FIXED; } return S; } if (Action & SYM_ALLOC_NEW) { /* Otherwise create a new entry, insert and return it. If the scope is ** already closed, mark the symbol as fixed so it won't be resolved ** by a symbol in the enclosing scopes later. */ SymEntry* N = NewSymEntry (Name, SF_NONE); if (SymTabIsClosed (Scope)) { N->Flags |= SF_FIXED; } N->Sym.Tab = Scope; if (S == 0) { Scope->Table[Hash] = N; } else if (Cmp < 0) { S->Left = N; } else { S->Right = N; } ++Scope->TableEntries; return N; } /* We did not find the entry and AllocNew is false. */ return 0; } SymEntry* SymFindAny (SymTable* Scope, const StrBuf* 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. */ { /* Generate the name hash */ unsigned Hash = HashBuf (Name); /* Search for the symbol */ SymEntry* Sym; do { /* Search in the current table. Ignore entries flagged with SF_UNUSED, ** because for such symbols there is a real entry in one of the parent ** scopes. */ if (SymSearchTree (Scope->Table[Hash % Scope->TableSlots], Name, &Sym) == 0) { if (Sym->Flags & SF_UNUSED) { Sym = 0; } else { /* Found, return it */ break; } } else { Sym = 0; } /* Not found, search in the parent scope, if we have one */ Scope = Scope->Parent; } while (Sym == 0 && Scope != 0); /* Return the result */ return Sym; } static void SymCheckUndefined (SymEntry* S) /* Handle an undefined symbol */ { /* Undefined symbol. It may be... ** ** - An undefined symbol in a nested lexical level. If the symbol is not ** fixed to this level, 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->Flags & SF_FIXED) == 0) { SymTable* Tab = GetSymParentScope (S); while (Tab) { Sym = SymFind (Tab, GetStrBuf (S->Name), SYM_FIND_EXISTING | SYM_CHECK_ONLY); if (Sym && (Sym->Flags & (SF_DEFINED | SF_IMPORT)) != 0) { /* We've found a symbol in a higher level that is ** either defined in the source, or an import. */ break; } /* No matching symbol found in this level. Look further */ Tab = Tab->Parent; } } if (Sym) { /* We found the symbol in a higher level. Transfer the flags and ** address size from the local symbol to that in the higher level ** and check for problems. */ if (S->Flags & SF_EXPORT) { if (Sym->Flags & SF_IMPORT) { /* The symbol is already marked as import */ LIError (&S->RefLines, "Symbol `%s' is already an import", GetString (Sym->Name)); } if ((Sym->Flags & SF_EXPORT) == 0) { /* Mark the symbol as an export */ Sym->Flags |= SF_EXPORT; Sym->ExportSize = S->ExportSize; if (Sym->ExportSize == ADDR_SIZE_DEFAULT) { /* Use the actual size of the symbol */ Sym->ExportSize = Sym->AddrSize; } if (Sym->AddrSize > Sym->ExportSize) { /* We're exporting a symbol smaller than it actually is */ LIWarning (&Sym->DefLines, 1, "Symbol `%m%p' is %s but exported %s", GetSymName (Sym), AddrSizeToStr (Sym->AddrSize), AddrSizeToStr (Sym->ExportSize)); } } } if (S->Flags & SF_REFERENCED) { /* Mark as referenced and move the line info */ Sym->Flags |= SF_REFERENCED; CollTransfer (&Sym->RefLines, &S->RefLines); CollDeleteAll (&S->RefLines); } /* Transfer all expression references */ SymTransferExprRefs (S, Sym); /* Mark the symbol as unused removing all other flags */ S->Flags = SF_UNUSED; } else { /* The symbol is definitely undefined */ if (S->Flags & SF_EXPORT) { /* We will not auto-import an export */ LIError (&S->RefLines, "Exported symbol `%m%p' was never defined", GetSymName (S)); } else { if (AutoImport) { /* Mark as import, will be indexed later */ S->Flags |= SF_IMPORT; /* Use the address size for code */ S->AddrSize = CodeAddrSize; /* Mark point of import */ GetFullLineInfo (&S->DefLines); } else { /* Error */ LIError (&S->RefLines, "Symbol `%m%p' is undefined", GetSymName (S)); } } } } 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) { if (S->Flags & SF_DEFINED) { SymExportFromGlobal (S); } else { SymImportFromGlobal (S); } } /* 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. Count exports and imports ** and set address sizes where this has not happened before. Ignore ** undefined's, since we handled them in the last pass, and ignore unused ** symbols, since we handled them in the last pass, too. */ S = SymList; while (S) { if ((S->Flags & SF_UNUSED) == 0 && (S->Flags & SF_UNDEFMASK) != SF_UNDEFVAL) { /* Check for defined symbols that were never referenced */ if (IsSizeOfSymbol (S)) { /* Remove line infos, we don't need them any longer */ ReleaseFullLineInfo (&S->DefLines); ReleaseFullLineInfo (&S->RefLines); } else if ((S->Flags & SF_DEFINED) != 0 && (S->Flags & SF_REFERENCED) == 0) { LIWarning (&S->DefLines, 2, "Symbol `%m%p' is defined but never used", GetSymName (S)); } /* Assign an index to all imports */ if (S->Flags & SF_IMPORT) { if ((S->Flags & (SF_REFERENCED | SF_FORCED)) == SF_NONE) { /* Imported symbol is not referenced */ LIWarning (&S->DefLines, 2, "Symbol `%m%p' is imported but never used", GetSymName (S)); } else { /* Give the import an id, count imports */ S->ImportId = ImportCount++; } } /* Count exports, assign the export ID */ if (S->Flags & SF_EXPORT) { S->ExportId = ExportCount++; } /* If the symbol is defined but has an unknown address size, ** recalculate it. */ if (SymHasExpr (S) && S->AddrSize == ADDR_SIZE_DEFAULT) { ExprDesc ED; ED_Init (&ED); StudyExpr (S->Expr, &ED); S->AddrSize = ED.AddrSize; if (SymIsExport (S)) { if (S->ExportSize == ADDR_SIZE_DEFAULT) { /* Use the real export size */ S->ExportSize = S->AddrSize; } else if (S->AddrSize > S->ExportSize) { /* We're exporting a symbol smaller than it actually is */ LIWarning (&S->DefLines, 1, "Symbol `%m%p' is %s but exported %s", GetSymName (S), AddrSizeToStr (S->AddrSize), AddrSizeToStr (S->ExportSize)); } } ED_Done (&ED); } /* If the address size of the symbol was guessed, check the guess ** against the actual address size and print a warning if the two ** differ. */ if (S->AddrSize != ADDR_SIZE_DEFAULT) { /* Do we have data for this address size? */ if (S->AddrSize <= sizeof (S->GuessedUse) / sizeof (S->GuessedUse[0])) { /* Get the file position where the symbol was used */ const FilePos* P = S->GuessedUse[S->AddrSize - 1]; if (P) { PWarning (P, 0, "Didn't use %s addressing for `%m%p'", AddrSizeToStr (S->AddrSize), GetSymName (S)); } } } } /* Next symbol */ S = S->List; } } void SymDump (FILE* F) /* Dump the symbol table */ { SymEntry* S = SymList; while (S) { /* Ignore unused symbols */ if ((S->Flags & SF_UNUSED) == 0) { fprintf (F, "%-24s %s %s %s %s %s\n", SB_GetConstBuf (GetSymName (S)), (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_UNUSED | SF_IMPORT)) == SF_IMPORT && (S->Flags & (SF_REFERENCED | SF_FORCED)) != 0) { ObjWrite8 (S->AddrSize); ObjWriteVar (S->Name); WriteLineInfo (&S->DefLines); WriteLineInfo (&S->RefLines); } 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_UNUSED | SF_EXPORT)) == SF_EXPORT) { /* Get the expression bits and the value */ long ConstVal; unsigned SymFlags = GetSymInfoFlags (S, &ConstVal); /* Check if this symbol has a size. If so, remember it in the ** flags. */ long Size; SymEntry* SizeSym = FindSizeOfSymbol (S); if (SizeSym != 0 && SymIsConst (SizeSym, &Size)) { SymFlags |= SYM_SIZE; } /* Count the number of ConDes types */ for (Type = 0; Type < CD_TYPE_COUNT; ++Type) { if (S->ConDesPrio[Type] != CD_PRIO_NONE) { SYM_INC_CONDES_COUNT (SymFlags); } } /* Write the type and the export size */ ObjWriteVar (SymFlags); ObjWrite8 (S->ExportSize); /* Write any ConDes declarations */ if (SYM_GET_CONDES_COUNT (SymFlags) > 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 (SYM_IS_CONST (SymFlags)) { /* Constant value */ ObjWrite32 (ConstVal); } else { /* Expression involved */ WriteExpr (S->Expr); } /* If the symbol has a size, write it to the file */ if (SYM_HAS_SIZE (SymFlags)) { ObjWriteVar (Size); } /* Write the line infos */ WriteLineInfo (&S->DefLines); WriteLineInfo (&S->RefLines); } 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, give each symbol an id and count them */ Count = 0; S = SymList; while (S) { if (IsDbgSym (S)) { S->DebugSymId = Count++; } S = S->List; } /* Write the symbol count to the list */ ObjWriteVar (Count); /* Walk through list and write all symbols to the file. Ignore size ** symbols. */ S = SymList; while (S) { if (IsDbgSym (S)) { /* Get the expression bits and the value */ long ConstVal; unsigned SymFlags = GetSymInfoFlags (S, &ConstVal); /* Check if this symbol has a size. If so, remember it in the ** flags. */ long Size; SymEntry* SizeSym = FindSizeOfSymbol (S); if (SizeSym != 0 && SymIsConst (SizeSym, &Size)) { SymFlags |= SYM_SIZE; } /* Write the type */ ObjWriteVar (SymFlags); /* Write the address size */ ObjWrite8 (S->AddrSize); /* Write the id of the parent. For normal symbols, this is a ** scope (symbol table), for cheap locals, it's a symbol. */ if (SYM_IS_STD (SymFlags)) { ObjWriteVar (S->Sym.Tab->Id); } else { ObjWriteVar (S->Sym.Entry->DebugSymId); } /* Write the name */ ObjWriteVar (S->Name); /* Write the value */ if (SYM_IS_CONST (SymFlags)) { /* Constant value */ ObjWrite32 (ConstVal); } else { /* Expression involved */ WriteExpr (S->Expr); } /* If the symbol has a size, write it to the file */ if (SYM_HAS_SIZE (SymFlags)) { ObjWriteVar (Size); } /* If the symbol is an im- or export, write out the ids */ if (SYM_IS_IMPORT (SymFlags)) { ObjWriteVar (GetSymImportId (S)); } if (SYM_IS_EXPORT (SymFlags)) { ObjWriteVar (GetSymExportId (S)); } /* Write the line infos */ WriteLineInfo (&S->DefLines); WriteLineInfo (&S->RefLines); } S = S->List; } } else { /* No debug symbols */ ObjWriteVar (0); } /* Write the high level symbols */ WriteHLLDbgSyms (); /* Done writing debug symbols */ ObjEndDbgSyms (); } void WriteScopes (void) /* Write the scope table to the object file */ { /* Tell the object file module that we're about to start the scopes */ ObjStartScopes (); /* We will write scopes only if debug symbols are requested */ if (DbgSyms) { /* Get head of list */ SymTable* S = RootScope; /* Write the scope count to the file */ ObjWriteVar (ScopeCount); /* Walk through all scopes and write them to the file */ while (S) { /* Flags for this scope */ unsigned Flags = 0; /* Check if this scope has a size. If so, remember it in the ** flags. */ long Size; SymEntry* SizeSym = FindSizeOfScope (S); if (SizeSym != 0 && SymIsConst (SizeSym, &Size)) { Flags |= SCOPE_SIZE; } /* Check if the scope has a label */ if (S->Label) { Flags |= SCOPE_LABELED; } /* Scope must be defined */ CHECK (S->Type != SCOPE_UNDEF); /* Id of parent scope */ if (S->Parent) { ObjWriteVar (S->Parent->Id); } else { ObjWriteVar (0); } /* Lexical level */ ObjWriteVar (S->Level); /* Scope flags */ ObjWriteVar (Flags); /* Type of scope */ ObjWriteVar (S->Type); /* Name of the scope */ ObjWriteVar (S->Name); /* If the scope has a size, write it to the file */ if (SCOPE_HAS_SIZE (Flags)) { ObjWriteVar (Size); } /* If the scope has a label, write its id to the file */ if (SCOPE_HAS_LABEL (Flags)) { ObjWriteVar (S->Label->DebugSymId); } /* Spans for this scope */ WriteSpanList (&S->Spans); /* Next scope */ S = S->Next; } } else { /* No debug info requested */ ObjWriteVar (0); } /* Done writing the scopes */ ObjEndScopes (); }