mirror of
https://github.com/cc65/cc65.git
synced 2025-03-03 09:32:33 +00:00
512 lines
15 KiB
C
512 lines
15 KiB
C
/*****************************************************************************/
|
|
/* */
|
|
/* lineinfo.c */
|
|
/* */
|
|
/* Source file line info structure */
|
|
/* */
|
|
/* */
|
|
/* */
|
|
/* (C) 2001-2011, Ullrich von Bassewitz */
|
|
/* Roemerstrasse 52 */
|
|
/* 70794 Filderstadt */
|
|
/* EMail: uz@cc65.org */
|
|
/* */
|
|
/* */
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
/* arising from the use of this software. */
|
|
/* */
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
/* freely, subject to the following restrictions: */
|
|
/* */
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
/* claim that you wrote the original software. If you use this software */
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
/* appreciated but is not required. */
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
/* be misrepresented as being the original software. */
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
/* distribution. */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.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"
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Forwards */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
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 LineInfoList = STATIC_COLLECTION_INITIALIZER;
|
|
|
|
/* Collection with currently active line infos */
|
|
static Collection CurLineInfo = STATIC_COLLECTION_INITIALIZER;
|
|
|
|
/* Hash table functions */
|
|
static const HashFunctions HashFunc = {
|
|
HT_GenHash,
|
|
HT_GetKey,
|
|
HT_Compare
|
|
};
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* struct LineInfo */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
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 */
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
static void FreeLineInfo (LineInfo* LI)
|
|
/* Free a LineInfo structure */
|
|
{
|
|
/* 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 */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
#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;
|
|
|
|
/* Increase the initial count of the line info collection */
|
|
CollGrow (&LineInfoList, 200);
|
|
|
|
/* Create a LineInfo for the default source. This is necessary to allow
|
|
** error message to be generated without any input file open.
|
|
*/
|
|
AsmLineInfo = StartLine (&DefaultPos, LI_TYPE_ASM, 0);
|
|
}
|
|
|
|
|
|
|
|
void DoneLineInfo (void)
|
|
/* Close down line infos */
|
|
{
|
|
/* Close all current line infos */
|
|
unsigned Count = CollCount (&CurLineInfo);
|
|
while (Count) {
|
|
EndLine (CollAt (&CurLineInfo, --Count));
|
|
}
|
|
|
|
/* 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 EndLine (LineInfo* LI)
|
|
/* End a line that is tracked by the given LineInfo structure */
|
|
{
|
|
/* Close the spans for the line */
|
|
CloseSpanList (&LI->OpenSpans);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
|
|
|
|
LineInfo* StartLine (const FilePos* Pos, unsigned Type, unsigned Count)
|
|
/* Start line info for a new line */
|
|
{
|
|
LineInfoKey Key;
|
|
LineInfo* LI;
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* 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 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.
|
|
*/
|
|
{
|
|
/* 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);
|
|
}
|
|
|
|
/* Start a new line using the current line info */
|
|
AsmLineInfo = StartLine (&CurTok.Pos, LI_TYPE_ASM, 0);
|
|
|
|
/* If the first LineInfo in the list came from a .dbg line, then we want
|
|
** errors and warnings to show it as an additional note, not as the primary
|
|
** line. Therefore, swap the first two LineInfo items.
|
|
*/
|
|
if (GetLineInfoType (CollAtUnchecked (&CurLineInfo, 0)) == LI_TYPE_EXT) {
|
|
CollMove (&CurLineInfo, 1, 0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LineInfo* GetAsmLineInfo (void)
|
|
/* Return the line info for the current assembler file. The function will
|
|
** bump the reference counter before returning the line info.
|
|
*/
|
|
{
|
|
++AsmLineInfo->RefCount;
|
|
return AsmLineInfo;
|
|
}
|
|
|
|
|
|
|
|
void ReleaseLineInfo (LineInfo* LI)
|
|
/* Decrease the reference count for a line info */
|
|
{
|
|
/* Decrease the reference counter */
|
|
CHECK (LI->RefCount > 0);
|
|
++LI->RefCount;
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
*/
|
|
{
|
|
unsigned I;
|
|
|
|
/* Bum the reference counter for all active line infos */
|
|
for (I = 0; I < CollCount (&CurLineInfo); ++I) {
|
|
++((LineInfo*)CollAt (&CurLineInfo, I))->RefCount;
|
|
}
|
|
|
|
/* Copy all line infos over */
|
|
CollTransfer (LineInfos, &CurLineInfo);
|
|
}
|
|
|
|
|
|
|
|
void ReleaseFullLineInfo (Collection* LineInfos)
|
|
/* Decrease the reference count for a collection full of LineInfos, then clear
|
|
** the collection.
|
|
*/
|
|
{
|
|
unsigned I;
|
|
|
|
/* Walk over all entries */
|
|
for (I = 0; I < CollCount (LineInfos); ++I) {
|
|
/* Release the line info */
|
|
ReleaseLineInfo (CollAt (LineInfos, I));
|
|
}
|
|
|
|
/* Delete all entries */
|
|
CollDeleteAll (LineInfos);
|
|
}
|
|
|
|
|
|
|
|
const FilePos* GetSourcePos (const LineInfo* LI)
|
|
/* Return the source file position from the given line info */
|
|
{
|
|
return &LI->Key.Pos;
|
|
}
|
|
|
|
|
|
|
|
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. */
|
|
{
|
|
unsigned I;
|
|
|
|
/* Write the count */
|
|
ObjWriteVar (CollCount (LineInfos));
|
|
|
|
/* Write the line info indices */
|
|
for (I = 0; I < CollCount (LineInfos); ++I) {
|
|
|
|
/* Get a pointer to the line info */
|
|
const LineInfo* LI = CollConstAt (LineInfos, I);
|
|
|
|
/* Safety */
|
|
CHECK (LI->Id != ~0U);
|
|
|
|
/* Write the index to the file */
|
|
ObjWriteVar (LI->Id);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void WriteLineInfos (void)
|
|
/* Write a list of all line infos to the object file. */
|
|
{
|
|
unsigned I;
|
|
|
|
/* Tell the object file module that we're about to write line infos */
|
|
ObjStartLineInfos ();
|
|
|
|
/* Write the line info count to the list */
|
|
ObjWriteVar (CollCount (&LineInfoList));
|
|
|
|
/* Walk over the list and write all line infos */
|
|
for (I = 0; I < CollCount (&LineInfoList); ++I) {
|
|
|
|
/* Get a pointer to this line info */
|
|
LineInfo* LI = CollAt (&LineInfoList, I);
|
|
|
|
/* Write the source file position */
|
|
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 */
|
|
ObjEndLineInfos ();
|
|
}
|