/*****************************************************************************/ /* */ /* lineinfo.c */ /* */ /* Source file line info structure */ /* */ /* */ /* */ /* (C) 2001-2011, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* 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 #include /* common */ #include "coll.h" #include "hashfunc.h" #include "xmalloc.h" /* ca65 */ #include "filetab.h" #include "global.h" #include "lineinfo.h" #include "objfile.h" #include "scanner.h" #include "span.h" /*****************************************************************************/ /* Forwards */ /*****************************************************************************/ static unsigned HT_GenHash (const void* Key); /* Generate the hash over a key. */ static const void* HT_GetKey (const void* Entry); /* Given a pointer to the user entry data, return a pointer to the key */ static int HT_Compare (const void* Key1, const void* Key2); /* Compare two keys. The function must return a value less than zero if ** Key1 is smaller than Key2, zero if both are equal, and a value greater ** than zero if Key1 is greater then Key2. */ /*****************************************************************************/ /* Data */ /*****************************************************************************/ /* Structure that holds the key for a line info */ typedef struct LineInfoKey LineInfoKey; struct LineInfoKey { FilePos Pos; /* File position */ unsigned Type; /* Type/count of line info */ }; /* Structure that holds line info */ struct LineInfo { HashNode Node; /* Hash table node */ unsigned Id; /* Index */ LineInfoKey Key; /* Key for this line info */ unsigned RefCount; /* Reference counter */ Collection Spans; /* Segment spans for this line info */ Collection OpenSpans; /* List of currently open spans */ }; /* Collection containing all line infos */ static Collection LineInfoList = STATIC_COLLECTION_INITIALIZER; /* Collection with currently active line infos */ static Collection CurLineInfo = STATIC_COLLECTION_INITIALIZER; /* Hash table functions */ static const HashFunctions HashFunc = { HT_GenHash, HT_GetKey, HT_Compare }; /* Line info hash table */ static HashTable LineInfoTab = STATIC_HASHTABLE_INITIALIZER (1051, &HashFunc); /* The current assembler input line */ static LineInfo* AsmLineInfo = 0; /*****************************************************************************/ /* Hash table functions */ /*****************************************************************************/ static unsigned HT_GenHash (const void* Key) /* Generate the hash over a key. */ { /* Key is a LineInfoKey pointer */ const LineInfoKey* K = Key; /* Hash over a combination of type, file and line */ return HashInt ((K->Type << 21) ^ (K->Pos.Name << 14) ^ K->Pos.Line); } static const void* HT_GetKey (const void* Entry) /* Given a pointer to the user entry data, return a pointer to the key */ { return &((const LineInfo*)Entry)->Key; } static int HT_Compare (const void* Key1, const void* Key2) /* Compare two keys. The function must return a value less than zero if ** Key1 is smaller than Key2, zero if both are equal, and a value greater ** than zero if Key1 is greater then Key2. */ { /* Convert both parameters to FileInfoKey pointers */ const LineInfoKey* K1 = Key1; const LineInfoKey* K2 = Key2; /* Compare line number, then file and type */ int Res = (int)K2->Pos.Line - (int)K1->Pos.Line; if (Res == 0) { Res = (int)K2->Pos.Name - (int)K1->Pos.Name; if (Res == 0) { Res = (int)K2->Type - (int)K1->Type; } } /* Done */ return Res; } /*****************************************************************************/ /* struct LineInfo */ /*****************************************************************************/ static LineInfo* NewLineInfo (const LineInfoKey* Key) /* Create and return a new line info. Usage will be zero. */ { /* Allocate memory */ LineInfo* LI = xmalloc (sizeof (LineInfo)); /* Initialize the fields */ InitHashNode (&LI->Node); LI->Id = ~0U; LI->Key = *Key; LI->RefCount = 0; InitCollection (&LI->Spans); InitCollection (&LI->OpenSpans); /* Add it to the hash table, so we will find it if necessary */ HT_Insert (&LineInfoTab, LI); /* Return the new struct */ return LI; } static void FreeLineInfo (LineInfo* LI) /* Free a LineInfo structure */ { /* Free the Spans collection. It is supposed to be empty */ CHECK (CollCount (&LI->Spans) == 0); DoneCollection (&LI->Spans); DoneCollection (&LI->OpenSpans); /* Free the structure itself */ xfree (LI); } static int CheckLineInfo (void* Entry, void* Data attribute ((unused))) /* Called from HT_Walk. Remembers used line infos and assigns them an id */ { /* Entry is actually a line info */ LineInfo* LI = Entry; /* The entry is used if there are spans or the ref counter is non zero */ if (LI->RefCount > 0 || CollCount (&LI->Spans) > 0) { LI->Id = CollCount (&LineInfoList); CollAppend (&LineInfoList, LI); return 0; /* Keep the entry */ } else { FreeLineInfo (LI); return 1; /* Remove entry from table */ } } /*****************************************************************************/ /* Code */ /*****************************************************************************/ #if 0 static void DumpLineInfos (const char* Title, const Collection* C) /* Dump line infos from the given collection */ { unsigned I; fprintf (stderr, "%s:\n", Title); for (I = 0; I < CollCount (C); ++I) { const LineInfo* LI = CollConstAt (C, I); const char* Type; switch (GetLineInfoType (LI)) { case LI_TYPE_ASM: Type = "ASM"; break; case LI_TYPE_EXT: Type = "EXT"; break; case LI_TYPE_MACRO: Type = "MACRO"; break; case LI_TYPE_MACPARAM: Type = "MACPARAM"; break; default: Type = "unknown"; break; } fprintf (stderr, "%2u: %-8s %2u %-16s %u/%u\n", I, Type, LI->Key.Pos.Name, SB_GetConstBuf (GetFileName (LI->Key.Pos.Name)), LI->Key.Pos.Line, LI->Key.Pos.Col); } } #endif void InitLineInfo (void) /* Initialize the line infos */ { static const FilePos DefaultPos = STATIC_FILEPOS_INITIALIZER; /* Increase the initial count of the line info collection */ CollGrow (&LineInfoList, 200); /* Create a LineInfo for the default source. This is necessary to allow ** error message to be generated without any input file open. */ AsmLineInfo = StartLine (&DefaultPos, LI_TYPE_ASM, 0); } void DoneLineInfo (void) /* Close down line infos */ { /* Close all current line infos */ unsigned Count = CollCount (&CurLineInfo); while (Count) { EndLine (CollAt (&CurLineInfo, --Count)); } /* Walk over the entries in the hash table and sort them into used and ** unused ones. Add the used ones to the line info list and assign them ** an id. */ HT_Walk (&LineInfoTab, CheckLineInfo, 0); } void EndLine (LineInfo* LI) /* End a line that is tracked by the given LineInfo structure */ { /* Close the spans for the line */ CloseSpanList (&LI->OpenSpans); /* Move the spans to the list of all spans for this line, then clear the ** list of open spans. */ CollTransfer (&LI->Spans, &LI->OpenSpans); CollDeleteAll (&LI->OpenSpans); /* Line info is no longer active - remove it from the list of current ** line infos. */ CollDeleteItem (&CurLineInfo, LI); } LineInfo* StartLine (const FilePos* Pos, unsigned Type, unsigned Count) /* Start line info for a new line */ { LineInfoKey Key; LineInfo* LI; /* Prepare the key struct */ Key.Pos = *Pos; Key.Type = LI_MAKE_TYPE (Type, Count); /* Try to find a line info with this position and type in the hash table. ** If so, reuse it. Otherwise create a new one. */ LI = HT_Find (&LineInfoTab, &Key); if (LI == 0) { /* Allocate a new LineInfo */ LI = NewLineInfo (&Key); } /* Open the spans for this line info */ OpenSpanList (&LI->OpenSpans); /* Add the line info to the list of current line infos */ CollAppend (&CurLineInfo, LI); /* Return the new info */ return LI; } void NewAsmLine (void) /* Start a new assembler input line. Use this function when generating new ** line of LI_TYPE_ASM. It will check if line and/or file have actually ** changed, end the old and start the new line as necessary. */ { /* Check if we can reuse the old line */ if (AsmLineInfo) { if (AsmLineInfo->Key.Pos.Line == CurTok.Pos.Line && AsmLineInfo->Key.Pos.Name == CurTok.Pos.Name) { /* We do already have line info for this line */ return; } /* Line has changed -> end the old line */ EndLine (AsmLineInfo); } /* Start a new line using the current line info */ AsmLineInfo = StartLine (&CurTok.Pos, LI_TYPE_ASM, 0); /* If the first LineInfo in the list came from a .dbg line, then we want ** errors and warnings to show it as an additional note, not as the primary ** line. Therefore, swap the first two LineInfo items. */ if (GetLineInfoType (CollAtUnchecked (&CurLineInfo, 0)) == LI_TYPE_EXT) { CollMove (&CurLineInfo, 1, 0); } } LineInfo* GetAsmLineInfo (void) /* Return the line info for the current assembler file. The function will ** bump the reference counter before returning the line info. */ { ++AsmLineInfo->RefCount; return AsmLineInfo; } void ReleaseLineInfo (LineInfo* LI) /* Decrease the reference count for a line info */ { /* Decrease the reference counter */ CHECK (LI->RefCount > 0); ++LI->RefCount; } void GetFullLineInfo (Collection* LineInfos) /* Return full line infos, that is line infos for currently active Slots. The ** infos will be added to the given collection, existing entries will be left ** intact. The reference count of all added entries will be increased. */ { unsigned I; /* Bum the reference counter for all active line infos */ for (I = 0; I < CollCount (&CurLineInfo); ++I) { ++((LineInfo*)CollAt (&CurLineInfo, I))->RefCount; } /* Copy all line infos over */ CollTransfer (LineInfos, &CurLineInfo); } void ReleaseFullLineInfo (Collection* LineInfos) /* Decrease the reference count for a collection full of LineInfos, then clear ** the collection. */ { unsigned I; /* Walk over all entries */ for (I = 0; I < CollCount (LineInfos); ++I) { /* Release the line info */ ReleaseLineInfo (CollAt (LineInfos, I)); } /* Delete all entries */ CollDeleteAll (LineInfos); } const FilePos* GetSourcePos (const LineInfo* LI) /* Return the source file position from the given line info */ { return &LI->Key.Pos; } unsigned GetLineInfoType (const LineInfo* LI) /* Return the type of a line info */ { return LI_GET_TYPE (LI->Key.Type); } void WriteLineInfo (const Collection* LineInfos) /* Write a list of line infos to the object file. */ { unsigned I; /* Write the count */ ObjWriteVar (CollCount (LineInfos)); /* Write the line info indices */ for (I = 0; I < CollCount (LineInfos); ++I) { /* Get a pointer to the line info */ const LineInfo* LI = CollConstAt (LineInfos, I); /* Safety */ CHECK (LI->Id != ~0U); /* Write the index to the file */ ObjWriteVar (LI->Id); } } void WriteLineInfos (void) /* Write a list of all line infos to the object file. */ { unsigned I; /* Tell the object file module that we're about to write line infos */ ObjStartLineInfos (); /* Write the line info count to the list */ ObjWriteVar (CollCount (&LineInfoList)); /* Walk over the list and write all line infos */ for (I = 0; I < CollCount (&LineInfoList); ++I) { /* Get a pointer to this line info */ LineInfo* LI = CollAt (&LineInfoList, I); /* Write the source file position */ ObjWritePos (&LI->Key.Pos); /* Write the type and count of the line info */ ObjWriteVar (LI->Key.Type); /* Write the ids of the spans for this line */ WriteSpanList (&LI->Spans); } /* End of line infos */ ObjEndLineInfos (); }