/*****************************************************************************/
/* */
-/* 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"
/* cc65 */
#include "asmlabel.h"
/*****************************************************************************/
-/* Data */
+/* Data */
/*****************************************************************************/
-#define LITPOOL_SIZE 4096 /* Max strings per function */
-static unsigned char LiteralPool[LITPOOL_SIZE]; /* The literal pool */
-static unsigned LiteralOffs = 0; /* Current pool offset */
-static unsigned LiteralSpace = 0; /* Space used (stats only) */
+/* 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 */
+};
-unsigned LiteralLabel = 1; /* Pool asm label */
+/* 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 TranslateLiteralPool (unsigned Offs)
-/* Translate the literals starting from the given offset into the target
- * charset.
- */
+static Literal* NewLiteral (const void* Buf, unsigned Len)
+/* Create a new literal and return it */
{
- while (Offs < LiteralOffs) {
- LiteralPool[Offs] = TgtTranslateChar (LiteralPool[Offs]);
- ++Offs;
- }
+ /* 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 DumpLiteralPool (void)
-/* Dump the literal pool */
+static void FreeLiteral (Literal* L)
+/* Free a literal */
{
- /* if nothing there, exit... */
- if (LiteralOffs == 0) {
- return;
- }
+ /* Free the literal data */
+ SB_Done (&L->Data);
- /* Switch to the data segment */
- if (WriteableStrings) {
- g_usedata ();
- } else {
- g_userodata ();
+ /* 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);
}
- /* Define the label */
- g_defloclabel (LiteralLabel);
+ /* Return the literal */
+ return L;
+}
+
+
+
+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;
+}
+
+
+
+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 */
+/*****************************************************************************/
+
+
- /* Translate the buffer contents into the target charset */
- TranslateLiteralPool (0);
+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;
+}
- /* Output the buffer data */
- g_defbytes (LiteralPool, LiteralOffs);
- /* Switch back to the code segment */
- g_usecode ();
- /* Reset the buffer */
- LiteralSpace += LiteralOffs; /* Count literal bytes emitted */
- LiteralLabel = GetLabel (); /* Get a new pool label */
- LiteralOffs = 0;
+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);
}
-unsigned GetLiteralOffs (void)
-/* Return the current offset into the literal pool */
+static int Compare (void* Data attribute ((unused)),
+ const void* Left, const void* Right)
+/* Compare function used when sorting the literal pool */
{
- return LiteralOffs;
+ /* Larger strings are considered "smaller" */
+ return (int) GetLiteralSize (Right) - (int) GetLiteralSize (Left);
}
-void ResetLiteralOffs (unsigned Offs)
-/* Reset the offset into the literal pool to some earlier value, effectively
- * removing values from the pool.
- */
+void InitLiteralPool (void)
+/* Initialize the literal pool */
{
- CHECK (Offs <= LiteralOffs);
- LiteralOffs = Offs;
+ /* Create the global literal pool */
+ GlobalPool = LP = NewLiteralPool (0);
}
-void AddLiteralChar (char C)
-/* Add one character to the literal pool */
+void PushLiteralPool (struct SymEntry* Func)
+/* Push the current literal pool onto the stack and create a new one */
{
- if (LiteralOffs >= LITPOOL_SIZE) {
- Fatal (FAT_OUT_OF_STRSPACE);
+ /* 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);
+ }
}
- LiteralPool[LiteralOffs++] = C;
}
-unsigned AddLiteral (const char* S)
-/* Add a literal string to the literal pool. Return the starting offset into
- * the pool
- */
+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 */
{
- /* Remember the starting offset */
- unsigned Start = LiteralOffs;
+ unsigned I;
+
+ /* If nothing there, exit... */
+ if (CollCount (Literals) == 0) {
+ return;
+ }
- /* Copy the string doing a range check */
- do {
- AddLiteralChar (*S);
- } while (*S++);
+ /* Switch to the correct segment */
+ g_usedata ();
- /* Return the starting offset */
- return Start;
+ /* 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);
+ }
+
+ }
}
-const char* GetLiteral (unsigned Offs)
-/* Get a pointer to the literal with the given offset in the pool */
+static void OutputReadOnlyLiterals (Collection* Literals)
+/* Output the given readonly literals merging (even partial) duplicates */
{
- CHECK (Offs < LiteralOffs);
- return (const char*) &LiteralPool[Offs];
+ 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) {
+
+ /* This literal is part of a longer literal, merge them */
+ g_aliasdatalabel (L->Label, C->Label, GetLiteralSize (C) - GetLiteralSize (L));
+
+ } 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 PrintLiteralStats (FILE* F)
-/* Print statistics about the literal space used */
+void OutputLiteralPool (void)
+/* Output the global literal pool */
{
- fprintf (F, "Literal space used: %d bytes\n", LiteralSpace);
+ /* Output both sorts of literals */
+ OutputWritableLiterals (&GlobalPool->WritableLiterals);
+ OutputReadOnlyLiterals (&GlobalPool->ReadOnlyLiterals);
}
+Literal* AddLiteral (const char* S)
+/* Add a literal string to the literal pool. Return the literal. */
+{
+ return AddLiteralBuf (S, strlen (S) + 1);
+}
+
+
+
+Literal* AddLiteralBuf (const void* Buf, unsigned Len)
+/* Add a buffer containing a literal string to the literal pool. Return the
+** literal.
+*/
+{
+ /* 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));
+}