]> git.sur5r.net Git - cc65/blobdiff - src/cc65/litpool.c
Fixed two compiler warnings.
[cc65] / src / cc65 / litpool.c
index 30c605d5fb7b1afee0407d3f737fe4c1651125bb..3d3aee745f3cfb458d755c3ede9b3a8b92020eda 100644 (file)
@@ -1,6 +1,6 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                                litpool.c                                 */
+/*                                litpool.c                                 */
 /*                                                                           */
 /*             Literal string handling for the cc65 C compiler              */
 /*                                                                           */
 
 
 #include <stdio.h>
+#include <string.h>
 
 /* common */
+#include "attrib.h"
 #include "check.h"
 #include "coll.h"
 #include "tgttrans.h"
 
 
 
-/* Forward for struct SymEntry */
-struct SymEntry;
+/* Definition of a literal */
+struct Literal {
+    unsigned    Label;                  /* Asm label for this literal */
+    int         RefCount;               /* Reference count */
+    int         Output;                 /* True if output has been generated */
+    StrBuf      Data;                   /* Literal data */
+};
 
 /* Definition of the literal pool */
-typedef struct LiteralPool LiteralPool;
 struct LiteralPool {
-    int                 Writable;       /* True if strings are writable */
-    unsigned            Label;          /* Pool asm label */
-    struct SymEntry*    Func;           /* Function that contains the pool */
-    StrBuf              Pool;           /* The pool itself */
+    struct SymEntry*    Func;               /* Function that owns the pool */
+    Collection          WritableLiterals;   /* Writable literals in the pool */
+    Collection          ReadOnlyLiterals;   /* Readonly literals in the pool */
 };
 
-/* The current literal pool */
-static LiteralPool*     LP = 0;
+/* The global and current literal pool */
+static LiteralPool*     GlobalPool = 0;
+static LiteralPool*     LP         = 0;
 
 /* Stack that contains the nested literal pools. Since TOS is in LiteralPool
  * and functions aren't nested in C, the maximum depth is 1. I'm using a
@@ -80,6 +86,134 @@ static Collection       LPStack  = STATIC_COLLECTION_INITIALIZER;
 
 
 
+/*****************************************************************************/
+/*                              struct Literal                               */
+/*****************************************************************************/
+
+
+
+static Literal* NewLiteral (const void* Buf, unsigned Len)
+/* Create a new literal and return it */
+{
+    /* Allocate memory */
+    Literal* L = xmalloc (sizeof (*L));
+
+    /* Initialize the fields */
+    L->Label    = GetLocalLabel ();
+    L->RefCount = 0;
+    L->Output   = 0;
+    SB_Init (&L->Data);
+    SB_AppendBuf (&L->Data, Buf, Len);
+
+    /* Return the new literal */
+    return L;
+}
+
+
+
+static void FreeLiteral (Literal* L)
+/* Free a literal */
+{
+    /* Free the literal data */
+    SB_Done (&L->Data);
+
+    /* Free the structure itself */
+    xfree (L);
+}
+
+
+
+static void OutputLiteral (Literal* L)
+/* Output one literal to the currently active data segment */
+{
+    /* Translate the literal into the target charset */
+    TranslateLiteral (L);
+
+    /* Define the label for the literal */
+    g_defdatalabel (L->Label);
+
+    /* Output the literal data */
+    g_defbytes (SB_GetConstBuf (&L->Data), SB_GetLen (&L->Data));
+
+    /* Mark the literal as output */
+    L->Output = 1;
+}
+
+
+
+Literal* UseLiteral (Literal* L)
+/* Increase the reference counter for the literal and return it */
+{
+    /* Increase the reference count */
+    ++L->RefCount;
+
+    /* If --local-strings was given, immediately output the literal */
+    if (IS_Get (&LocalStrings)) {
+        /* Switch to the proper data segment */
+        if (IS_Get (&WritableStrings)) {
+            g_usedata ();
+        } else {
+            g_userodata ();
+        }
+        /* Output the literal */
+        OutputLiteral (L);
+    }
+
+    /* Return the literal */
+    return L;
+}
+
+
+
+void ReleaseLiteral (Literal* L)
+/* Decrement the reference counter for the literal */
+{
+    --L->RefCount;
+    CHECK (L->RefCount >= 0 && (L->RefCount > 0 || !L->Output));
+}
+
+
+
+void TranslateLiteral (Literal* L)
+/* Translate a literal into the target charset. */
+{
+    TgtTranslateBuf (SB_GetBuf (&L->Data), SB_GetLen (&L->Data));
+}
+
+
+
+unsigned GetLiteralLabel (const Literal* L)
+/* Return the asm label for a literal */
+{
+    return L->Label;
+}
+
+
+
+const char* GetLiteralStr (const Literal* L)
+/* Return the data for a literal as pointer to char */
+{
+    return SB_GetConstBuf (&L->Data);
+}
+
+
+
+const StrBuf* GetLiteralStrBuf (const Literal* L)
+/* Return the data for a literal as pointer to the string buffer */
+{
+    return &L->Data;
+}
+
+
+
+unsigned GetLiteralSize (const Literal* L)
+/* Get the size of a literal string */
+{
+    return SB_GetLen (&L->Data);
+}
+
+
+
 /*****************************************************************************/
 /*                                  Code                                    */
 /*****************************************************************************/
@@ -93,10 +227,9 @@ static LiteralPool* NewLiteralPool (struct SymEntry* Func)
     LiteralPool* LP = xmalloc (sizeof (*LP));
 
     /* Initialize the fields */
-    LP->Writable = IS_Get (&WritableStrings);
-    LP->Label = GetLocalLabel ();
     LP->Func  = Func;
-    SB_Init (&LP->Pool);
+    InitCollection (&LP->WritableLiterals);
+    InitCollection (&LP->ReadOnlyLiterals);
 
     /* Return the new pool */
     return LP;
@@ -107,8 +240,9 @@ static LiteralPool* NewLiteralPool (struct SymEntry* Func)
 static void FreeLiteralPool (LiteralPool* LP)
 /* Free a LiteralPool structure */
 {
-    /* Free the string buffer contained within the struct */
-    SB_Done (&LP->Pool);
+    /* Free the collections contained within the struct */
+    DoneCollection (&LP->WritableLiterals);
+    DoneCollection (&LP->ReadOnlyLiterals);
 
     /* Free the struct itself */
     xfree (LP);
@@ -116,11 +250,21 @@ static void FreeLiteralPool (LiteralPool* LP)
 
 
 
+static int Compare (void* Data attribute ((unused)),
+                    const void* Left, const void* Right)
+/* Compare function used when sorting the literal pool */
+{
+    /* Larger strings are considered "smaller" */
+    return (int) GetLiteralSize (Right) - (int) GetLiteralSize (Left);
+}
+
+
+
 void InitLiteralPool (void)
 /* Initialize the literal pool */
 {
-    /* Create a new pool */
-    LP = NewLiteralPool (0);
+    /* Create the global literal pool */
+    GlobalPool = LP = NewLiteralPool (0);
 }
 
 
@@ -140,143 +284,219 @@ void PushLiteralPool (struct SymEntry* Func)
 
 
 
-void PopLiteralPool (void)
-/* Free the current literal pool and restore the one from TOS */
+LiteralPool* PopLiteralPool (void)
+/* Pop the last literal pool from TOS and activate it. Return the old
+ * literal pool.
+ */
 {
-    /* Free the current literal pool */
-    FreeLiteralPool (LP);
+    /* Remember the current literal pool */
+    LiteralPool* Old = LP;
 
     /* Pop one from stack */
     LP = CollPop (&LPStack);
+
+    /* Return the old one */
+    return Old;
 }
 
 
 
-void TranslateLiteralPool (unsigned Offs)
-/* Translate the literals starting from the given offset into the target
- * charset.
+static void MoveLiterals (Collection* Source, Collection* Target)
+/* Move referenced literals from Source to Target, delete unreferenced ones */
+{
+    unsigned I;
+
+    /* Move referenced literals, remove unreferenced ones */
+    for (I = 0; I < CollCount (Source); ++I) {
+
+        /* Get the literal */
+        Literal* L = CollAt (Source, I);
+
+        /* If it is referenced and not output, add it to the Target pool,
+         * otherwise free it
+         */
+        if (L->RefCount && !L->Output) {
+            CollAppend (Target, L);
+        } else {
+            FreeLiteral (L);
+        }
+    }
+}
+
+
+
+void MoveLiteralPool (LiteralPool* LocalPool)
+/* Move all referenced literals in LocalPool to the global literal pool. This
+ * function will free LocalPool after moving the used string literals.
  */
 {
-    TgtTranslateBuf (SB_GetBuf (&LP->Pool) + Offs, SB_GetLen (&LP->Pool) - Offs);
+    /* Move the literals */
+    MoveLiterals (&LocalPool->WritableLiterals, &GlobalPool->WritableLiterals);
+    MoveLiterals (&LocalPool->ReadOnlyLiterals, &GlobalPool->ReadOnlyLiterals);
+
+    /* Free the local literal pool */
+    FreeLiteralPool (LocalPool);
 }
 
 
 
-void DumpLiteralPool (void)
-/* Dump the literal pool */
+static void OutputWritableLiterals (Collection* Literals)
+/* Output the given writable literals */
 {
+    unsigned I;
+
     /* If nothing there, exit... */
-    if (SB_GetLen (&LP->Pool) == 0) {
-       return;
+    if (CollCount (Literals) == 0) {
+       return;
     }
 
     /* Switch to the correct segment */
-    if (LP->Writable) {
-       g_usedata ();
-    } else {
-               g_userodata ();
-    }
+    g_usedata ();
+
+    /* Emit all literals that have a reference */
+    for (I = 0; I < CollCount (Literals); ++I) {
 
-    /* Define the label */
-    g_defdatalabel (LP->Label);
+        /* Get a pointer to the literal */
+        Literal* L = CollAtUnchecked (Literals, I);
 
-    /* Translate the buffer contents into the target charset */
-    TranslateLiteralPool (0);
+        /* Output this one, if it has references and wasn't already output */
+        if (L->RefCount > 0 && !L->Output) {
+            OutputLiteral (L);
+        }
 
-    /* Output the buffer data */
-    g_defbytes (SB_GetConstBuf (&LP->Pool), SB_GetLen (&LP->Pool));
+    }
 }
 
 
 
-unsigned GetLiteralPoolLabel (void)
-/* Return the asm label for the current literal pool */
+static void OutputReadOnlyLiterals (Collection* Literals)
+/* Output the given readonly literals merging (even partial) duplicates */
 {
-    return LP->Label;
-}
+    unsigned I;
+
+    /* If nothing there, exit... */
+    if (CollCount (Literals) == 0) {
+       return;
+    }
 
+    /* Switch to the correct segment */
+    g_userodata ();
 
+    /* Sort the literal pool by literal size. Larger strings go first */
+    CollSort (Literals, Compare, 0);
 
-unsigned GetLiteralPoolOffs (void)
-/* Return the current offset into the literal pool */
-{
-    return SB_GetLen (&LP->Pool);
-}
+    /* Emit all literals that have a reference */
+    for (I = 0; I < CollCount (Literals); ++I) {
 
+        unsigned J;
+        Literal* C;
 
+        /* Get the next literal */
+        Literal* L = CollAt (Literals, I);
 
-void ResetLiteralPoolOffs (unsigned Offs)
-/* Reset the offset into the literal pool to some earlier value, effectively
- * removing values from the pool.
- */
-{
-    CHECK (Offs <= SB_GetLen (&LP->Pool));
-    SB_Cut (&LP->Pool, Offs);
-}
+        /* Ignore it, if it doesn't have references or was already output */
+        if (L->RefCount == 0 || L->Output) {
+            continue;
+        }
 
+        /* Translate the literal into the target charset */
+        TranslateLiteral (L);
 
+        /* Check if this literal is part of another one. Since the literals
+         * are sorted by size (larger ones first), it can only be part of a
+         * literal with a smaller index.
+         * Beware: Only check literals that have actually been referenced.
+         */
+        C = 0;
+        for (J = 0; J < I; ++J) {
 
-unsigned AddLiteral (const char* S)
-/* Add a literal string to the literal pool. Return the starting offset into
- * the pool
- */
-{
-    return AddLiteralBuf (S, strlen (S) + 1);
-}
+            const void* D;
 
+            /* Get a pointer to the compare literal */
+            Literal* L2 = CollAt (Literals, J);
 
+            /* Ignore literals that have no reference */
+            if (L2->RefCount == 0) {
+                continue;
+            }
 
-unsigned AddLiteralBuf (const void* Buf, unsigned Len)
-/* Add a buffer containing a literal string to the literal pool. Return the
- * starting offset into the pool for this string.
- */
-{
-    /* Remember the starting offset */
-    unsigned Start = SB_GetLen (&LP->Pool);
+            /* Get a pointer to the data */
+            D = SB_GetConstBuf (&L2->Data) + SB_GetLen (&L2->Data) - SB_GetLen (&L->Data);
+
+            /* Compare the data */
+            if (memcmp (D, SB_GetConstBuf (&L->Data), SB_GetLen (&L->Data)) == 0) {
+                /* Remember the literal and terminate the loop */
+                C = L2;
+                break;
+            }
+        }
+
+        /* Check if we found a match */
+        if (C != 0) {
 
-    /* Append the buffer */
-    SB_AppendBuf (&LP->Pool, Buf, Len);
+            /* This literal is part of a longer literal, merge them */
+            g_aliasdatalabel (L->Label, C->Label, GetLiteralSize (C) - GetLiteralSize (L));
 
-    /* Return the starting offset */
-    return Start;
+        } else {
+
+            /* Define the label for the literal */
+            g_defdatalabel (L->Label);
+
+            /* Output the literal data */
+            g_defbytes (SB_GetConstBuf (&L->Data), SB_GetLen (&L->Data));
+
+        }
+
+        /* Mark the literal */
+        L->Output = 1;
+    }
 }
 
 
 
-unsigned AddLiteralStr (const StrBuf* S)
-/* Add a literal string to the literal pool. Return the starting offset into
- * the pool for this string.
- */
+void OutputLiteralPool (void)
+/* Output the global literal pool */
 {
-    return AddLiteralBuf (SB_GetConstBuf (S), SB_GetLen (S));
+    /* Output both sorts of literals */
+    OutputWritableLiterals (&GlobalPool->WritableLiterals);
+    OutputReadOnlyLiterals (&GlobalPool->ReadOnlyLiterals);
 }
 
 
 
-const char* GetLiteral (unsigned Offs)
-/* Get a pointer to the literal with the given offset in the pool */
+Literal* AddLiteral (const char* S)
+/* Add a literal string to the literal pool. Return the literal. */
 {
-    CHECK (Offs < SB_GetLen (&LP->Pool));
-    return SB_GetConstBuf (&LP->Pool) + Offs;
+    return AddLiteralBuf (S, strlen (S) + 1);
 }
 
 
 
-void GetLiteralStrBuf (StrBuf* Target, unsigned Offs)
-/* Copy the string starting at Offs and lasting to the end of the buffer
- * into Target.
+Literal* AddLiteralBuf (const void* Buf, unsigned Len)
+/* Add a buffer containing a literal string to the literal pool. Return the
+ * literal.
  */
 {
-    CHECK (Offs <= SB_GetLen (&LP->Pool));
-    SB_Slice (Target, &LP->Pool, Offs, SB_GetLen (&LP->Pool) - Offs);
+    /* Create a new literal */
+    Literal* L = NewLiteral (Buf, Len);
+
+    /* Add the literal to the correct pool */
+    if (IS_Get (&WritableStrings)) {
+        CollAppend (&LP->WritableLiterals, L);
+    } else {
+        CollAppend (&LP->ReadOnlyLiterals, L);
+    }
+
+    /* Return the new literal */
+    return L;
 }
 
 
 
-void PrintLiteralPoolStats (FILE* F)
-/* Print statistics about the literal space used */
+Literal* AddLiteralStr (const StrBuf* S)
+/* Add a literal string to the literal pool. Return the literal. */
 {
-    fprintf (F, "Literal space used: %u bytes\n", SB_GetLen (&LP->Pool));
+    return AddLiteralBuf (SB_GetConstBuf (S), SB_GetLen (S));
 }