/* */
/* */
/* */
-/* (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 */
-/* 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 <string.h>
/* common */
#include "coll.h"
+#include "hashfunc.h"
#include "xmalloc.h"
/* ca65 */
-#include "objfile.h"
+#include "global.h"
#include "lineinfo.h"
+#include "objfile.h"
+#include "scanner.h"
+#include "span.h"
/*****************************************************************************/
-/* Data */
+/* 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 */
-Collection LineInfoColl = STATIC_COLLECTION_INITIALIZER;
-unsigned LineInfoValid = 0; /* Valid, that is, used entries */
+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
+};
-/* Static pointer to last line info or NULL if not active */
-LineInfo* CurLineInfo = 0;
+/* Line info hash table */
+static HashTable LineInfoTab = STATIC_HASHTABLE_INITIALIZER (1051, &HashFunc);
+
+/* The current assembler input line */
+static LineInfo* AsmLineInfo = 0;
/*****************************************************************************/
-/* Code */
+/* Hash table functions */
/*****************************************************************************/
-static LineInfo* NewLineInfo (unsigned FileIndex, unsigned long LineNum)
+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;
+ }
+ }
+
+ /* 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 */
- LI->Usage = 0;
- LI->Index = 0; /* Currently invalid */
- LI->Pos.Line = LineNum;
- LI->Pos.Col = 0;
- LI->Pos.Name = FileIndex;
+ InitHashNode (&LI->Node);
+ LI->Id = ~0U;
+ LI->Key = *Key;
+ LI->RefCount = 0;
+ InitCollection (&LI->Spans);
+ InitCollection (&LI->OpenSpans);
- /* Insert this structure into the collection */
- CollAppend (&LineInfoColl, LI);
+ /* Add it to the hash table, so we will find it if necessary */
+ HT_InsertEntry (&LineInfoTab, LI);
/* Return the new struct */
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.
- */
+static void FreeLineInfo (LineInfo* LI)
+/* Free a LineInfo structure */
{
- if (LI) {
- if (LI->Usage++ == 0) {
- /* One more valid line info */
- ++LineInfoValid;
- }
+ /* 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 */
}
- return LI;
}
-void GenLineInfo (unsigned FileIndex, unsigned long LineNum)
-/* Generate a new line info */
+/*****************************************************************************/
+/* Code */
+/*****************************************************************************/
+
+
+
+void InitLineInfo (void)
+/* Initialize the line infos */
{
- /* Create a new line info and make it current */
- CurLineInfo = NewLineInfo (FileIndex, LineNum);
+ 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 ClearLineInfo (void)
-/* Clear the current line info */
+void DoneLineInfo (void)
+/* Close down line infos */
{
- CurLineInfo = 0;
+ /* 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);
}
-static int CmpLineInfo (void* Data attribute ((unused)),
- const void* LI1_, const void* LI2_)
-/* Compare function for the sort */
+void EndLine (LineInfo* LI)
+/* End a line that is tracked by the given LineInfo structure */
{
- /* Cast the pointers */
- const LineInfo* LI1 = LI1_;
- const LineInfo* LI2 = LI2_;
+ unsigned I;
+
+ /* Close the spans for the line */
+ CloseSpans (&LI->OpenSpans);
- /* Unreferenced line infos are always larger, otherwise sort by file,
- * then by line.
+ /* Move the spans to the list of all spans for this line, then clear the
+ * list of open spans.
*/
- if ((LI1->Usage == 0) == (LI2->Usage == 0)) {
- /* Both are either referenced or unreferenced */
- if (LI1->Pos.Name< LI2->Pos.Name) {
- return -1;
- } else if (LI1->Pos.Name > LI2->Pos.Name) {
- return 1;
- } else if (LI1->Pos.Line < LI2->Pos.Line) {
- return -1;
- } else if (LI1->Pos.Line > LI2->Pos.Line) {
- return 1;
- } else {
- return 0;
- }
- } else {
- if (LI1->Usage > 0) {
- return -1;
- } else {
- return 1;
- }
+ 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;
+}
+
+
+
+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);
+}
+
+
+
+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 MakeLineInfoIndex (void)
-/* Sort the line infos and drop all unreferenced ones */
+
+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;
- /* Sort the collection */
- CollSort (&LineInfoColl, CmpLineInfo, 0);
+ /* If the collection is currently empty, grow it as necessary */
+ if (CollCount (LineInfos) == 0) {
+ CollGrow (LineInfos, CollCount (&CurLineInfo));
+ }
- /* Walk over the list and index the line infos. */
- for (I = 0; I < LineInfoValid; ++I) {
- /* Get a pointer to this line info */
- LineInfo* LI = CollAtUnchecked (&LineInfoColl, I);
- LI->Index = I;
+ /* 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 WriteLineInfo (void)
+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 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 ();
- /* Check if debug info is requested */
- if (DbgSyms) {
-
- unsigned I;
+ /* Write the line info count to the list */
+ ObjWriteVar (CollCount (&LineInfoList));
- /* Write the line info count to the list */
- ObjWriteVar (LineInfoValid);
+ /* Walk over the list and write all line infos */
+ for (I = 0; I < CollCount (&LineInfoList); ++I) {
- /* Walk through list and write all line infos that have references.
- * Because of the sort, this are exactly the first LineInfoValid
- * ones.
- */
- for (I = 0; I < LineInfoValid; ++I) {
- /* Get a pointer to this line info */
- LineInfo* LI = CollAtUnchecked (&LineInfoColl, I);
- /* Write the source file position */
- ObjWritePos (&LI->Pos);
- }
+ /* Get a pointer to this line info */
+ LineInfo* LI = CollAt (&LineInfoList, I);
- } else {
+ /* Write the source file position */
+ ObjWritePos (&LI->Key.Pos);
- /* No line infos */
- ObjWriteVar (0);
+ /* 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);
}