]> git.sur5r.net Git - cc65/blobdiff - src/ca65/lineinfo.c
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / src / ca65 / lineinfo.c
index 7e7ab3a04d3e091c5967341d91ab262ceda28799..dbc5b4d412c9de66846797ed7cfe855b5c002064 100644 (file)
@@ -1,8 +1,8 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                               lineinfo.c                                 */
+/*                                lineinfo.c                                 */
 /*                                                                           */
-/*                     Source file line info structure                      */
+/*                      Source file line info structure                      */
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
 
 
 
+#include <stdio.h>
 #include <string.h>
-#include <limits.h>
 
 /* 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"
 
 
 
 /*****************************************************************************/
-/*                                  Data                                    */
+/*                                 Forwards                                  */
 /*****************************************************************************/
 
 
 
-/* An invalid line info index */
-#define INV_LINEINFO_INDEX      UINT_MAX
+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 LineInfoColl = STATIC_COLLECTION_INITIALIZER;
+static Collection LineInfoList = STATIC_COLLECTION_INITIALIZER;
 
-/* Number of valid (=used) line infos in LineInfoColl */
-static unsigned UsedLineInfoCount;
+/* Collection with currently active line infos */
+static Collection CurLineInfo = STATIC_COLLECTION_INITIALIZER;
 
-/* Entry in CurLineInfo */
-typedef struct LineInfoSlot LineInfoSlot;
-struct LineInfoSlot {
-    unsigned    Type;
-    LineInfo*   Info;
+/* Hash table functions */
+static const HashFunctions HashFunc = {
+    HT_GenHash,
+    HT_GetKey,
+    HT_Compare
 };
 
-/* Dynamically allocated array of LineInfoSlots */
-static LineInfoSlot* CurLineInfo;
-static unsigned AllocatedSlots;
-static unsigned UsedSlots;
+/* 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;
+}
 
 
 
@@ -82,17 +174,22 @@ static unsigned UsedSlots;
 
 
 
-static LineInfo* NewLineInfo (unsigned Type, const FilePos* Pos)
+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->Type    = Type;
-    LI->Index   = INV_LINEINFO_INDEX;
-    LI->Pos     = *Pos;
+    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;
@@ -101,208 +198,257 @@ static LineInfo* NewLineInfo (unsigned Type, const FilePos* Pos)
 
 
 static void FreeLineInfo (LineInfo* LI)
-/* "Free" line info. If the usage counter is non zero, move it to the
- * collection that contains all line infos, otherwise delete it.
- * The function handles a NULL pointer transparently.
- */
+/* Free a LineInfo structure */
 {
-    if (LI) {
-        if (LI->Usage > 0) {
-            CollAppend (&LineInfoColl, LI);
-        } else {
-            xfree (LI);
-        }
+    /* 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                                    */
+/*                                   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;
 
-    /* Allocate 8 slots */
-    AllocatedSlots = 8;
-    CurLineInfo = xmalloc (AllocatedSlots * sizeof (LineInfoSlot));
+    /* Increase the initial count of the line info collection */
+    CollGrow (&LineInfoList, 200);
 
-    /* Initalize the predefined slots. Be sure to ccreate a new LineInfo for
-     * the default source. This is necessary to allow error message to be
-     * generated without any input file open.
+    /* Create a LineInfo for the default source. This is necessary to allow
+     * error message to be generated without any input file open.
      */
-    UsedSlots = 2;
-    CurLineInfo[LI_SLOT_ASM].Type = LI_TYPE_ASM;
-    CurLineInfo[LI_SLOT_ASM].Info = NewLineInfo (LI_TYPE_ASM, &DefaultPos);
-    CurLineInfo[LI_SLOT_EXT].Type = LI_TYPE_EXT;
-    CurLineInfo[LI_SLOT_EXT].Info = 0;
+    AsmLineInfo = StartLine (&DefaultPos, LI_TYPE_ASM, 0);
 }
 
 
 
-unsigned AllocLineInfoSlot (unsigned Type)
-/* Allocate a line info slot of the given type and return the slot index */
+void DoneLineInfo (void)
+/* Close down line infos */
 {
-    /* Grow the array if necessary */
-    if (UsedSlots >= AllocatedSlots) {
-        LineInfoSlot* NewLineInfo;
-        AllocatedSlots *= 2;
-        NewLineInfo = xmalloc (AllocatedSlots * sizeof (LineInfoSlot));
-        memcpy (NewLineInfo, CurLineInfo, UsedSlots * sizeof (LineInfoSlot));
-        xfree (CurLineInfo);
-        CurLineInfo = NewLineInfo;
+    /* Close all current line infos */
+    unsigned Count = CollCount (&CurLineInfo);
+    while (Count) {
+        EndLine (CollAt (&CurLineInfo, --Count));
     }
 
-    /* Array is now big enough, add the new data */
-    CurLineInfo[UsedSlots].Type = Type;
-    CurLineInfo[UsedSlots].Info = 0;
-
-    /* Increment the count and return the index of the new slot */
-    return UsedSlots++;
+    /* 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 FreeLineInfoSlot (unsigned Slot)
-/* Free the line info in the given slot. Note: Alloc/Free must be used in
- * FIFO order.
- */
+void EndLine (LineInfo* LI)
+/* End a line that is tracked by the given LineInfo structure */
 {
-    /* Check the parameter */
-    PRECONDITION (Slot == UsedSlots - 1);
+    /* Close the spans for the line */
+    CloseSpanList (&LI->OpenSpans);
 
-    /* Free the last entry */
-    FreeLineInfo (CurLineInfo[Slot].Info);
-    --UsedSlots;
+    /* 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);
 }
 
 
 
-void GenLineInfo (unsigned Slot, const FilePos* Pos)
-/* Generate a new line info in the given slot */
+LineInfo* StartLine (const FilePos* Pos, unsigned Type, unsigned Count)
+/* Start line info for a new line */
 {
-    /* Get a pointer to the slot */
-    LineInfoSlot* S = CurLineInfo + Slot;
-
-    /* Check if we already have data */
-    if (S->Info) {
-        /* Generate new data only if it is different from the existing. */
-        if (CompareFilePos (&S->Info->Pos, Pos) == 0) {
-            /* Already there */
-            return;
-        }
+    LineInfoKey Key;
+    LineInfo* LI;
 
-        /* We have data, but it's not identical. If it is in use, copy it to
-         * line info collection, otherwise delete it.
-         */
-        FreeLineInfo (S->Info);
+    /* 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);
     }
 
-    /* Allocate new data */
-    S->Info = NewLineInfo (S->Type, Pos);
+    /* 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 ClearLineInfo (unsigned Slot)
-/* Clear the line info in the given slot */
+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.
+ */
 {
-    /* Get a pointer to the slot */
-    LineInfoSlot* S = CurLineInfo + Slot;
+    /* 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);
+    }
 
-    /* Free the struct and zero the pointer */
-    FreeLineInfo (S->Info);
-    S->Info = 0;
+    /* Start a new line using the current line info */
+    AsmLineInfo = StartLine (&CurTok.Pos, LI_TYPE_ASM, 0);
 }
 
 
 
-void GetFullLineInfo (Collection* LineInfos)
-/* Return full line infos, that is line infos for all slots in LineInfos. The
- * function does also increase the usage counter for all line infos returned.
+LineInfo* GetAsmLineInfo (void)
+/* Return the line info for the current assembler file. The function will
+ * bump the reference counter before returning the line info.
  */
 {
-   unsigned I;
+    ++AsmLineInfo->RefCount;
+    return AsmLineInfo;
+}
 
-    /* Copy all valid line infos to the collection */
-    for (I = 0; I < UsedSlots; ++I) {
 
-        /* Get the slot */
-        LineInfoSlot* S = CurLineInfo + I;
 
-        /* Ignore empty slots */
-        if (S->Info) {
-            ++S->Info->Usage;
-            CollAppend (LineInfos, S->Info);
-        }
-    }
+void ReleaseLineInfo (LineInfo* LI)
+/* Decrease the reference count for a line info */
+{
+    /* Decrease the reference counter */
+    CHECK (LI->RefCount > 0);
+    ++LI->RefCount;
 }
 
 
 
-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 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.
  */
 {
-    if (LI) {
-               ++LI->Usage;
+    unsigned I;
+
+    /* Bum the reference counter for all active line infos */
+    for (I = 0; I < CollCount (&CurLineInfo); ++I) {
+        ++((LineInfo*)CollAt (&CurLineInfo, I))->RefCount;
     }
-    return LI;
+
+    /* Copy all line infos over */
+    CollTransfer (LineInfos, &CurLineInfo);
 }
 
 
 
-LineInfo* ReleaseLineInfo (LineInfo* LI)
-/* Decrease 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 ReleaseFullLineInfo (Collection* LineInfos)
+/* Decrease the reference count for a collection full of LineInfos, then clear
+ * the collection.
  */
 {
-    if (LI) {
-        /* Cannot decrease below zero */
-               CHECK (LI->Usage != 0);
-        --LI->Usage;
+    unsigned I;
+
+    /* Walk over all entries */
+    for (I = 0; I < CollCount (LineInfos); ++I) {
+        /* Release the the line info */
+        ReleaseLineInfo (CollAt (LineInfos, I));
     }
-    return LI;
+
+    /* Delete all entries */
+    CollDeleteAll (LineInfos);
 }
 
 
 
-static int CmpLineInfo (void* Data attribute ((unused)),
-                       const void* LI1_, const void* LI2_)
-/* Compare function for the sort */
+const FilePos* GetSourcePos (const LineInfo* LI)
+/* Return the source file position from the given line info */
 {
-    /* Cast the pointers */
-    const LineInfo* LI1 = LI1_;
-    const LineInfo* LI2 = LI2_;
+    return &LI->Key.Pos;
+}
 
-    /* Unreferenced line infos are always larger, otherwise sort by file,
-     * then by line, then by column.
-     */
-    if ((LI1->Usage == 0) == (LI2->Usage == 0)) {
-       /* Both are either referenced or unreferenced */
-        return CompareFilePos (&LI1->Pos, &LI2->Pos);
-    } else {
-       if (LI1->Usage > 0) {
-           return -1;
-       } else {
-           return 1;
-       }
-    }
+
+
+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. MakeLineInfoIndex has to
- * be called before!
- */
+/* Write a list of line infos to the object file. */
 {
     unsigned I;
 
@@ -315,45 +461,11 @@ void WriteLineInfo (const Collection* LineInfos)
         /* Get a pointer to the line info */
         const LineInfo* LI = CollConstAt (LineInfos, I);
 
-        /* Check the index */
-        CHECK (LI->Index != INV_LINEINFO_INDEX);
+        /* Safety */
+        CHECK (LI->Id != ~0U);
 
         /* Write the index to the file */
-        ObjWriteVar (LI->Index);
-    }
-}
-
-
-
-void MakeLineInfoIndex (void)
-/* Index the line infos */
-{
-    unsigned I;
-
-    /* Be sure to move pending line infos to the global list */
-    for (I = 0; I < UsedSlots; ++I) {
-        FreeLineInfo (CurLineInfo[I].Info);
-    }
-
-    /* Sort the collection */
-    CollSort (&LineInfoColl, CmpLineInfo, 0);
-
-    /* Walk over the list, index the line infos and count the used ones */
-    UsedLineInfoCount = 0;
-    for (I = 0; I < CollCount (&LineInfoColl); ++I) {
-       /* Get a pointer to this line info */
-       LineInfo* LI = CollAtUnchecked (&LineInfoColl, I);
-
-        /* If it is invalid, terminate the loop. All unused line infos were
-         * placed at the end of the collection by the sort.
-         */
-        if (LI->Usage == 0) {
-            break;
-        }
-
-        /* Index and count this one */
-        LI->Index = I;
-        ++UsedLineInfoCount;
+        ObjWriteVar (LI->Id);
     }
 }
 
@@ -368,14 +480,22 @@ void WriteLineInfos (void)
     ObjStartLineInfos ();
 
     /* Write the line info count to the list */
-    ObjWriteVar (UsedLineInfoCount);
+    ObjWriteVar (CollCount (&LineInfoList));
 
     /* Walk over the list and write all line infos */
-    for (I = 0; I < UsedLineInfoCount; ++I) {
+    for (I = 0; I < CollCount (&LineInfoList); ++I) {
+
         /* Get a pointer to this line info */
-        LineInfo* LI = CollAt (&LineInfoColl, I);
+        LineInfo* LI = CollAt (&LineInfoList, I);
+
         /* Write the source file position */
-        ObjWritePos (&LI->Pos);
+        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 */