X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fca65%2Flineinfo.c;h=cddd17ac3c0a652b92318a85ed83561cadf3dd14;hb=c52916f461f5f723bc8f2146e346dcb2de3b2f18;hp=5634c5a9b41143ce9fb6ca94ccb8807254e6ce76;hpb=ea2cf602b0961d149b140ccb963500b3ed81c2f5;p=cc65 diff --git a/src/ca65/lineinfo.c b/src/ca65/lineinfo.c index 5634c5a9b..cddd17ac3 100644 --- a/src/ca65/lineinfo.c +++ b/src/ca65/lineinfo.c @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 2001 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@cc65.org */ +/* (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 */ @@ -33,114 +33,470 @@ -/* Note: The line infos kept here are additional line infos supplied by the - * ".dbg line" command. The native line infos are always kept in the fragments - * itself (because one fragment always originates from one line). The - * additional line infos (which may not exist if none are supplied in the - * source) may have several fragments attached (as is the case with sources - * generated by the C compiler). - */ - - +#include /* common */ #include "coll.h" +#include "hashfunc.h" #include "xmalloc.h" /* ca65 */ +#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 */ +/* Data */ /*****************************************************************************/ -/* Linked list of all line infos */ -LineInfo* LineInfoRoot = 0; -LineInfo* LineInfoLast = 0; -unsigned LineInfoCount = 0; -unsigned LineInfoValid = 0; /* Valid, that is, used entries */ +/* 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 << 18) ^ (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; + } + } -/* Static pointer to last line info or NULL if not active */ -LineInfo* CurLineInfo = 0; + /* Done */ + return Res; +} /*****************************************************************************/ -/* Code */ +/* struct LineInfo */ /*****************************************************************************/ -static LineInfo* NewLineInfo (unsigned FileIndex, unsigned long LineNum) +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 */ - LI->Next = 0; - LI->Usage = 0; - LI->Index = 0; /* Currently invalid */ - LI->Pos.Line = LineNum; - LI->Pos.Col = 0; - LI->Pos.Name = FileIndex; - - /* Insert this structure into the line info list */ - if (LineInfoLast == 0) { - LineInfoRoot = LI; + 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_InsertEntry (&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 { - LineInfoLast->Next = LI; + FreeLineInfo (LI); + return 1; /* Remove entry from table */ } - LineInfoLast = LI; +} - /* Count the line infos */ - ++LineInfoCount; - /* Return the new struct */ + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +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 */ +{ + unsigned I; + + /* Close the spans for the line */ + CloseSpans (&LI->OpenSpans); + + /* Move the spans to the list of all spans for this line, then clear the + * list of open spans. + */ + for (I = 0; I < CollCount (&LI->OpenSpans); ++I) { + CollAppend (&LI->Spans, CollAtUnchecked (&LI->OpenSpans, I)); + } + 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_FindEntry (&LineInfoTab, &Key); + if (LI == 0) { + /* Allocate a new LineInfo */ + LI = NewLineInfo (&Key); + } + + /* Open the spans for this line info */ + OpenSpans (&LI->OpenSpans); + + /* Add the line info to the list of current line infos */ + CollAppend (&CurLineInfo, LI); + + /* Return the new info */ return LI; } -LineInfo* UseLineInfo (LineInfo* LI) -/* Increase the reference count of the given line info and return it. The - * function will gracefully accept NULL pointers and do nothing in this case. +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. */ { - if (LI) { - ++LI->Usage; + /* 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); } - return LI; + + /* Start a new line using the current line info */ + AsmLineInfo = StartLine (&CurTok.Pos, LI_TYPE_ASM, 0); } -void GenLineInfo (unsigned FileIndex, unsigned long LineNum) -/* Generate a new line info */ +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 */ { - /* Create a new line info and make it current */ - CurLineInfo = NewLineInfo (FileIndex, LineNum); + /* 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; + + /* If the collection is currently empty, grow it as necessary */ + if (CollCount (LineInfos) == 0) { + CollGrow (LineInfos, CollCount (&CurLineInfo)); + } + + /* Copy all valid line infos to the collection */ + for (I = 0; I < CollCount (&CurLineInfo); ++I) { + + /* Get the line info from the slot */ + LineInfo* LI = CollAt (&CurLineInfo, I); + + /* Bump the reference counter */ + ++LI->RefCount; + + /* Return it to the caller */ + CollAppend (LineInfos, LI); + } } -void MakeLineInfoIndex (void) -/* Walk over the line info list and make an index of all entries ignoring - * those with a usage count of zero. +void ReleaseFullLineInfo (Collection* LineInfos) +/* Decrease the reference count for a collection full of LineInfos, then clear + * the collection. */ { - LineInfo* LI = LineInfoRoot; - LineInfoValid = 0; - while (LI) { - if (LI->Usage) { - LI->Index = LineInfoValid++; - } - LI = LI->Next; + unsigned I; + + /* Walk over all entries */ + for (I = 0; I < CollCount (LineInfos); ++I) { + /* Release the 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; + + Collection EmptySpans = STATIC_COLLECTION_INITIALIZER; + + /* 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); + + /* Spans are only added to the debug file if debug information is + * requested. Otherwise we write an empty list. + */ + if (DbgSyms) { + WriteSpans (&LI->Spans); + } else { + /* Write out an empty list */ + WriteSpans (&EmptySpans); + } + } + + /* End of line infos */ + ObjEndLineInfos (); + + /* For the sake of completeness, but not really necessary */ + DoneCollection (&EmptySpans); }