/*****************************************************************************/
/* */
-/* 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 */
+ 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
+/*****************************************************************************/
+/* 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;
+ 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);
+}
+
+
+
+Literal* UseLiteral (Literal* L)
+/* Increase the reference counter for the literal and return it */
+{
+ ++L->RefCount;
+ return L;
+}
+
+
+
+void ReleaseLiteral (Literal* L)
+/* Decrement the reference counter for the literal */
+{
+ 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 */
/*****************************************************************************/
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;
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);
+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);
}
-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, add it to the Target pool, otherwise free it */
+ if (L->RefCount) {
+ 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 DumpWritableLiterals (Collection* Literals)
+/* Dump 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 ();
- /* Define the label */
- g_defdatalabel (LP->Label);
+ /* Emit all literals that have a reference */
+ for (I = 0; I < CollCount (Literals); ++I) {
- /* Translate the buffer contents into the target charset */
- TranslateLiteralPool (0);
+ /* Get the next literal */
+ Literal* L = CollAt (Literals, I);
- /* Output the buffer data */
- g_defbytes (SB_GetConstBuf (&LP->Pool), SB_GetLen (&LP->Pool));
-}
+ /* Ignore it, if it doesn't have references */
+ if (L->RefCount == 0) {
+ continue;
+ }
+ /* Translate the literal into the target charset */
+ TranslateLiteral (L);
+ /* Define the label for the literal */
+ g_defdatalabel (L->Label);
-unsigned GetLiteralPoolLabel (void)
-/* Return the asm label for the current literal pool */
-{
- return LP->Label;
+ /* Output the literal data */
+ g_defbytes (SB_GetConstBuf (&L->Data), SB_GetLen (&L->Data));
+
+ }
}
-unsigned GetLiteralPoolOffs (void)
-/* Return the current offset into the literal pool */
+static void DumpReadOnlyLiterals (Collection* Literals)
+/* Dump the given readonly literals merging (even partial) duplicates */
{
- return SB_GetLen (&LP->Pool);
-}
+ unsigned I;
+ /* If nothing there, exit... */
+ if (CollCount (Literals) == 0) {
+ return;
+ }
+ /* Switch to the correct segment */
+ g_userodata ();
-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);
-}
+ /* 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;
-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);
-}
+ /* Get the next literal */
+ Literal* L = CollAt (Literals, I);
+ /* Ignore it, if it doesn't have references */
+ if (L->RefCount == 0) {
+ continue;
+ }
+ /* Translate the literal into the target charset */
+ TranslateLiteral (L);
-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);
+ /* 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));
- /* Append the buffer */
- SB_AppendBuf (&LP->Pool, Buf, Len);
- /* 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));
+
+ }
+ }
}
-unsigned AddLiteralStr (const StrBuf* S)
-/* Add a literal string to the literal pool. Return the starting offset into
- * the pool for this string.
- */
+void DumpLiteralPool (void)
+/* Dump the global literal pool */
{
- return AddLiteralBuf (SB_GetConstBuf (S), SB_GetLen (S));
+ /* Dump both sorts of literals */
+ DumpWritableLiterals (&GlobalPool->WritableLiterals);
+ DumpReadOnlyLiterals (&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));
}