]> git.sur5r.net Git - cc65/blobdiff - src/cc65/litpool.c
Fixed the CBM screen-code C header.
[cc65] / src / cc65 / litpool.c
index 2ba2d745658693bdb4b29fd6a4d80df6f1f2c18e..c427310d951515eb88f31028621df2e0ccaed838 100644 (file)
@@ -1,15 +1,15 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                                litpool.c                                 */
+/*                                 litpool.c                                 */
 /*                                                                           */
-/*             Literal string handling for the cc65 C compiler              */
+/*              Literal string handling for the cc65 C compiler              */
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1998     Ullrich von Bassewitz                                        */
-/*              Wacholderweg 14                                              */
-/*              D-70597 Stuttgart                                            */
-/* EMail:       uz@musoftware.de                                             */
+/* (C) 1998-2013, Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
 /*                                                                           */
 /*                                                                           */
 /* This software is provided 'as-is', without any expressed or implied       */
 
 
 #include <stdio.h>
+#include <string.h>
 
 /* common */
+#include "attrib.h"
 #include "check.h"
+#include "coll.h"
 #include "tgttrans.h"
 #include "xmalloc.h"
 
 
 
 /*****************************************************************************/
-/*                                  Data                                    */
+/*                                   Data                                    */
 /*****************************************************************************/
 
 
 
-static unsigned char* LiteralPoolBuf   = 0;    /* Pointer to buffer */
-static unsigned       LiteralPoolSize  = 0;    /* Size of pool */
-static unsigned              LiteralPoolOffs   = 0;    /* Current offset into pool */
-unsigned             LiteralPoolLabel  = 0;    /* Pool asm label */
+/* 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 */
+struct LiteralPool {
+    struct SymEntry*    Func;               /* Function that owns the pool */
+    Collection          WritableLiterals;   /* Writable literals in the pool */
+    Collection          ReadOnlyLiterals;   /* Readonly literals in the pool */
+};
+
+/* 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
+** collection anyway, so the code is prepared for nested functions or
+** whatever.
+*/
+static Collection       LPStack  = STATIC_COLLECTION_INITIALIZER;
 
 
 
 /*****************************************************************************/
-/*                                  Code                                    */
+/*                              struct Literal                               */
 /*****************************************************************************/
 
 
 
-void InitLiteralPool (void)
-/* Initialize the literal pool */
+static Literal* NewLiteral (const void* Buf, unsigned Len)
+/* Create a new literal and return it */
 {
-    /* Get the pool label */
-    LiteralPoolLabel = GetLocalLabel ();
+    /* 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;
 }
 
 
 
-void TranslateLiteralPool (unsigned Offs)
-/* Translate the literals starting from the given offset into the target
- * charset.
- */
+static void FreeLiteral (Literal* L)
+/* Free a literal */
 {
-    TgtTranslateBuf (LiteralPoolBuf + Offs, LiteralPoolOffs - Offs);
+    /* Free the literal data */
+    SB_Done (&L->Data);
+
+    /* Free the structure itself */
+    xfree (L);
 }
 
 
 
-void DumpLiteralPool (void)
-/* Dump the literal pool */
+static void OutputLiteral (Literal* L)
+/* Output one literal to the currently active data segment */
 {
-    /* If nothing there, exit... */
-    if (LiteralPoolOffs == 0) {
-       return;
-    }
+    /* Translate the literal into the target charset */
+    TranslateLiteral (L);
 
-    /* Switch to the data segment */
-    if (WriteableStrings) {
-       g_usedata ();
-    } else {
-               g_userodata ();
+    /* 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);
     }
 
-    /* Define the label */
-    g_defloclabel (LiteralPoolLabel);
+    /* Return the literal */
+    return L;
+}
 
-    /* Translate the buffer contents into the target charset */
-    TranslateLiteralPool (0);
 
-    /* Output the buffer data */
-    g_defbytes (LiteralPoolBuf, LiteralPoolOffs);
 
-    /* Switch back to the code segment */
-    g_usecode ();
+void ReleaseLiteral (Literal* L)
+/* Decrement the reference counter for the literal */
+{
+    --L->RefCount;
+    CHECK (L->RefCount >= 0);
+}
+
+
+
+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;
 }
 
 
 
-unsigned GetLiteralPoolOffs (void)
-/* Return the current offset into the literal pool */
+const char* GetLiteralStr (const Literal* L)
+/* Return the data for a literal as pointer to char */
 {
-    return LiteralPoolOffs;
+    return SB_GetConstBuf (&L->Data);
 }
 
 
 
-void ResetLiteralPoolOffs (unsigned Offs)
-/* Reset the offset into the literal pool to some earlier value, effectively
- * removing values from the pool.
- */
+const StrBuf* GetLiteralStrBuf (const Literal* L)
+/* Return the data for a literal as pointer to the string buffer */
 {
-    CHECK (Offs <= LiteralPoolOffs);
-    LiteralPoolOffs = Offs;
+    return &L->Data;
 }
 
 
 
-void AddLiteralChar (char C)
-/* Add one character to the literal pool */
+unsigned GetLiteralSize (const Literal* L)
+/* Get the size of a literal string */
 {
-    /* Grow the buffer if needed */
-    if (LiteralPoolOffs >= LiteralPoolSize) {
-       if (LiteralPoolSize == 0) {
-           /* First call */
-           LiteralPoolSize = 256;
-       } else {
-           LiteralPoolSize *= 2;
-       }
-               LiteralPoolBuf = xrealloc (LiteralPoolBuf, LiteralPoolSize);
+    return SB_GetLen (&L->Data);
+}
+
+
+
+/*****************************************************************************/
+/*                                   Code                                    */
+/*****************************************************************************/
+
+
+
+static LiteralPool* NewLiteralPool (struct SymEntry* Func)
+/* Create a new literal pool and return it */
+{
+    /* Allocate memory */
+    LiteralPool* LP = xmalloc (sizeof (*LP));
+
+    /* Initialize the fields */
+    LP->Func  = Func;
+    InitCollection (&LP->WritableLiterals);
+    InitCollection (&LP->ReadOnlyLiterals);
+
+    /* Return the new pool */
+    return LP;
+}
+
+
+
+static void FreeLiteralPool (LiteralPool* LP)
+/* Free a LiteralPool structure */
+{
+    /* Free the collections contained within the struct */
+    DoneCollection (&LP->WritableLiterals);
+    DoneCollection (&LP->ReadOnlyLiterals);
+
+    /* Free the struct itself */
+    xfree (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 the global literal pool */
+    GlobalPool = LP = NewLiteralPool (0);
+}
+
+
+
+void PushLiteralPool (struct SymEntry* Func)
+/* Push the current literal pool onto the stack and create a new one */
+{
+    /* We must have a literal pool to push! */
+    PRECONDITION (LP != 0);
+
+    /* Push the old pool */
+    CollAppend (&LPStack, LP);
+
+    /* Create a new one */
+    LP = NewLiteralPool (Func);
+}
+
+
+
+LiteralPool* PopLiteralPool (void)
+/* Pop the last literal pool from TOS and activate it. Return the old
+** literal pool.
+*/
+{
+    /* Remember the current literal pool */
+    LiteralPool* Old = LP;
+
+    /* Pop one from stack */
+    LP = CollPop (&LPStack);
+
+    /* Return the old one */
+    return Old;
+}
+
+
+
+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.
+*/
+{
+    /* Move the literals */
+    MoveLiterals (&LocalPool->WritableLiterals, &GlobalPool->WritableLiterals);
+    MoveLiterals (&LocalPool->ReadOnlyLiterals, &GlobalPool->ReadOnlyLiterals);
+
+    /* Free the local literal pool */
+    FreeLiteralPool (LocalPool);
+}
+
+
+
+static void OutputWritableLiterals (Collection* Literals)
+/* Output the given writable literals */
+{
+    unsigned I;
+
+    /* If nothing there, exit... */
+    if (CollCount (Literals) == 0) {
+        return;
     }
 
-    /* Store the character */
-    LiteralPoolBuf[LiteralPoolOffs++] = C;
+    /* Switch to the correct segment */
+    g_usedata ();
+
+    /* Emit all literals that have a reference */
+    for (I = 0; I < CollCount (Literals); ++I) {
+
+        /* Get a pointer to the literal */
+        Literal* L = CollAtUnchecked (Literals, I);
+
+        /* Output this one, if it has references and wasn't already output */
+        if (L->RefCount > 0 && !L->Output) {
+            OutputLiteral (L);
+        }
+
+    }
 }
 
 
 
-unsigned AddLiteral (const char* S)
-/* Add a literal string to the literal pool. Return the starting offset into
- * the pool
- */
+static void OutputReadOnlyLiterals (Collection* Literals)
+/* Output the given readonly literals merging (even partial) duplicates */
 {
-    /* Remember the starting offset */
-    unsigned Start = LiteralPoolOffs;
+    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);
+
+    /* 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);
+
+        /* 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) {
+
+            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;
+            }
+
+            /* 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) {
 
-    /* Copy the string including the terminator growing the buffer if needed */
-    do {
-       AddLiteralChar (*S);
-    } while (*S++);
+            /* 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;
+    }
+}
+
+
+
+void OutputLiteralPool (void)
+/* Output the global literal pool */
+{
+    /* 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 < LiteralPoolOffs);
-    return (const char*) &LiteralPoolBuf[Offs];
+    return AddLiteralBuf (S, strlen (S) + 1);
 }
 
 
 
-void PrintLiteralPoolStats (FILE* F)
-/* Print statistics about the literal space used */
+Literal* AddLiteralBuf (const void* Buf, unsigned Len)
+/* Add a buffer containing a literal string to the literal pool. Return the
+** literal.
+*/
 {
-    fprintf (F, "Literal space used: %u bytes\n", LiteralPoolOffs);
+    /* 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;
 }
 
 
 
+Literal* AddLiteralStr (const StrBuf* S)
+/* Add a literal string to the literal pool. Return the literal. */
+{
+    return AddLiteralBuf (SB_GetConstBuf (S), SB_GetLen (S));
+}