1/*****************************************************************************/
2/* */
3/* lineinfo.c */
4/* */
5/* Source file line info structure */
6/* */
7/* */
8/* */
9/* (C) 2001-2011, Ullrich von Bassewitz */
10/* Roemerstrasse 52 */
11/* 70794 Filderstadt */
12/* EMail: uz@cc65.org */
13/* */
14/* */
15/* This software is provided 'as-is', without any expressed or implied */
16/* warranty. In no event will the authors be held liable for any damages */
17/* arising from the use of this software. */
18/* */
19/* Permission is granted to anyone to use this software for any purpose, */
20/* including commercial applications, and to alter it and redistribute it */
21/* freely, subject to the following restrictions: */
22/* */
23/* 1. The origin of this software must not be misrepresented; you must not */
24/* claim that you wrote the original software. If you use this software */
25/* in a product, an acknowledgment in the product documentation would be */
26/* appreciated but is not required. */
27/* 2. Altered source versions must be plainly marked as such, and must not */
28/* be misrepresented as being the original software. */
29/* 3. This notice may not be removed or altered from any source */
30/* distribution. */
31/* */
32/*****************************************************************************/
33
34
35
36#include <stdio.h>
37#include <string.h>
38
39/* common */
40#include "coll.h"
41#include "hashfunc.h"
42#include "xmalloc.h"
43
44/* ca65 */
45#include "filetab.h"
46#include "global.h"
47#include "lineinfo.h"
48#include "objfile.h"
49#include "scanner.h"
50#include "span.h"
51
52
53
54/*****************************************************************************/
55/* Forwards */
56/*****************************************************************************/
57
58
59
60static unsigned HT_GenHash (const void* Key);
61/* Generate the hash over a key. */
62
63static const void* HT_GetKey (const void* Entry);
64/* Given a pointer to the user entry data, return a pointer to the key */
65
66static int HT_Compare (const void* Key1, const void* Key2);
67/* Compare two keys. The function must return a value less than zero if
68** Key1 is smaller than Key2, zero if both are equal, and a value greater
69** than zero if Key1 is greater then Key2.
70*/
71
72
73
74/*****************************************************************************/
75/* Data */
76/*****************************************************************************/
77
78
79
80/* Structure that holds the key for a line info */
81typedef struct LineInfoKey LineInfoKey;
82struct LineInfoKey {
83 FilePos Pos; /* File position */
84 unsigned Type; /* Type/count of line info */
85};
86
87/* Structure that holds line info */
88struct LineInfo {
89 HashNode Node; /* Hash table node */
90 unsigned Id; /* Index */
91 LineInfoKey Key; /* Key for this line info */
92 unsigned RefCount; /* Reference counter */
93 Collection Spans; /* Segment spans for this line info */
94 Collection OpenSpans; /* List of currently open spans */
95};
96
97
98
99/* Collection containing all line infos */
100static Collection LineInfoList = STATIC_COLLECTION_INITIALIZER;
101
102/* Collection with currently active line infos */
103static Collection CurLineInfo = STATIC_COLLECTION_INITIALIZER;
104
105/* Hash table functions */
106static const HashFunctions HashFunc = {
107 HT_GenHash,
108 HT_GetKey,
109 HT_Compare
110};
111
112/* Line info hash table */
113static HashTable LineInfoTab = STATIC_HASHTABLE_INITIALIZER (1051, &HashFunc);
114
115/* The current assembler input line */
116static LineInfo* AsmLineInfo = 0;
117
118
119
120/*****************************************************************************/
121/* Hash table functions */
122/*****************************************************************************/
123
124
125
126static unsigned HT_GenHash (const void* Key)
127/* Generate the hash over a key. */
128{
129 /* Key is a LineInfoKey pointer */
130 const LineInfoKey* K = Key;
131
132 /* Hash over a combination of type, file and line */
133 return HashInt ((K->Type << 21) ^ (K->Pos.Name << 14) ^ K->Pos.Line);
134}
135
136
137
138static const void* HT_GetKey (const void* Entry)
139/* Given a pointer to the user entry data, return a pointer to the key */
140{
141 return &((const LineInfo*)Entry)->Key;
142}
143
144
145
146static int HT_Compare (const void* Key1, const void* Key2)
147/* Compare two keys. The function must return a value less than zero if
148** Key1 is smaller than Key2, zero if both are equal, and a value greater
149** than zero if Key1 is greater then Key2.
150*/
151{
152 /* Convert both parameters to FileInfoKey pointers */
153 const LineInfoKey* K1 = Key1;
154 const LineInfoKey* K2 = Key2;
155
156 /* Compare line number, then file and type */
157 int Res = (int)K2->Pos.Line - (int)K1->Pos.Line;
158 if (Res == 0) {
159 Res = (int)K2->Pos.Name - (int)K1->Pos.Name;
160 if (Res == 0) {
161 Res = (int)K2->Type - (int)K1->Type;
162 }
163 }
164
165 /* Done */
166 return Res;
167}
168
169
170
171/*****************************************************************************/
172/* struct LineInfo */
173/*****************************************************************************/
174
175
176
177static LineInfo* NewLineInfo (const LineInfoKey* Key)
178/* Create and return a new line info. Usage will be zero. */
179{
180 /* Allocate memory */
181 LineInfo* LI = xmalloc (sizeof (LineInfo));
182
183 /* Initialize the fields */
184 InitHashNode (&LI->Node);
185 LI->Id = ~0U;
186 LI->Key = *Key;
187 LI->RefCount = 0;
188 InitCollection (&LI->Spans);
189 InitCollection (&LI->OpenSpans);
190
191 /* Add it to the hash table, so we will find it if necessary */
192 HT_Insert (&LineInfoTab, LI);
193
194 /* Return the new struct */
195 return LI;
196}
197
198
199
200static void FreeLineInfo (LineInfo* LI)
201/* Free a LineInfo structure */
202{
203 /* Free the Spans collection. It is supposed to be empty */
204 CHECK (CollCount (&LI->Spans) == 0);
205 DoneCollection (&LI->Spans);
206 DoneCollection (&LI->OpenSpans);
207
208 /* Free the structure itself */
209 xfree (LI);
210}
211
212
213
214static int CheckLineInfo (void* Entry, void* Data attribute ((unused)))
215/* Called from HT_Walk. Remembers used line infos and assigns them an id */
216{
217 /* Entry is actually a line info */
218 LineInfo* LI = Entry;
219
220 /* The entry is used if there are spans or the ref counter is non zero */
221 if (LI->RefCount > 0 || CollCount (&LI->Spans) > 0) {
222 LI->Id = CollCount (&LineInfoList);
223 CollAppend (&LineInfoList, LI);
224 return 0; /* Keep the entry */
225 } else {
226 FreeLineInfo (LI);
227 return 1; /* Remove entry from table */
228 }
229}
230
231
232
233/*****************************************************************************/
234/* Code */
235/*****************************************************************************/
236
237
238
239#if 0
240static void DumpLineInfos (const char* Title, const Collection* C)
241/* Dump line infos from the given collection */
242{
243 unsigned I;
244 fprintf (stderr, "%s:\n", Title);
245 for (I = 0; I < CollCount (C); ++I) {
246 const LineInfo* LI = CollConstAt (C, I);
247 const char* Type;
248 switch (GetLineInfoType (LI)) {
249 case LI_TYPE_ASM: Type = "ASM"; break;
250 case LI_TYPE_EXT: Type = "EXT"; break;
251 case LI_TYPE_MACRO: Type = "MACRO"; break;
252 case LI_TYPE_MACPARAM: Type = "MACPARAM"; break;
253 default: Type = "unknown"; break;
254 }
255 fprintf (stderr,
256 "%2u: %-8s %2u %-16s %u/%u\n",
257 I, Type, LI->Key.Pos.Name,
258 SB_GetConstBuf (GetFileName (LI->Key.Pos.Name)),
259 LI->Key.Pos.Line, LI->Key.Pos.Col);
260 }
261}
262#endif
263
264
265
266void InitLineInfo (void)
267/* Initialize the line infos */
268{
269 static const FilePos DefaultPos = STATIC_FILEPOS_INITIALIZER;
270
271 /* Increase the initial count of the line info collection */
272 CollGrow (&LineInfoList, 200);
273
274 /* Create a LineInfo for the default source. This is necessary to allow
275 ** error message to be generated without any input file open.
276 */
277 AsmLineInfo = StartLine (&DefaultPos, LI_TYPE_ASM, 0);
278}
279
280
281
282void DoneLineInfo (void)
283/* Close down line infos */
284{
285 /* Close all current line infos */
286 unsigned Count = CollCount (&CurLineInfo);
287 while (Count) {
288 EndLine (CollAt (&CurLineInfo, --Count));
289 }
290
291 /* Walk over the entries in the hash table and sort them into used and
292 ** unused ones. Add the used ones to the line info list and assign them
293 ** an id.
294 */
295 HT_Walk (&LineInfoTab, CheckLineInfo, 0);
296}
297
298
299
300void EndLine (LineInfo* LI)
301/* End a line that is tracked by the given LineInfo structure */
302{
303 /* Close the spans for the line */
304 CloseSpanList (&LI->OpenSpans);
305
306 /* Move the spans to the list of all spans for this line, then clear the
307 ** list of open spans.
308 */
309 CollTransfer (&LI->Spans, &LI->OpenSpans);
310 CollDeleteAll (&LI->OpenSpans);
311
312 /* Line info is no longer active - remove it from the list of current
313 ** line infos.
314 */
315 CollDeleteItem (&CurLineInfo, LI);
316}
317
318
319
320LineInfo* StartLine (const FilePos* Pos, unsigned Type, unsigned Count)
321/* Start line info for a new line */
322{
323 LineInfoKey Key;
324 LineInfo* LI;
325
326 /* Prepare the key struct */
327 Key.Pos = *Pos;
328 Key.Type = LI_MAKE_TYPE (Type, Count);
329
330 /* Try to find a line info with this position and type in the hash table.
331 ** If so, reuse it. Otherwise create a new one.
332 */
333 LI = HT_Find (&LineInfoTab, &Key);
334 if (LI == 0) {
335 /* Allocate a new LineInfo */
336 LI = NewLineInfo (&Key);
337 }
338
339 /* Open the spans for this line info */
340 OpenSpanList (&LI->OpenSpans);
341
342 /* Add the line info to the list of current line infos */
343 CollAppend (&CurLineInfo, LI);
344
345 /* Return the new info */
346 return LI;
347}
348
349
350
351void NewAsmLine (void)
352/* Start a new assembler input line. Use this function when generating new
353** line of LI_TYPE_ASM. It will check if line and/or file have actually
354** changed, end the old and start the new line as necessary.
355*/
356{
357 /* Check if we can reuse the old line */
358 if (AsmLineInfo) {
359 if (AsmLineInfo->Key.Pos.Line == CurTok.Pos.Line &&
360 AsmLineInfo->Key.Pos.Name == CurTok.Pos.Name) {
361 /* We do already have line info for this line */
362 return;
363 }
364
365 /* Line has changed -> end the old line */
366 EndLine (AsmLineInfo);
367 }
368
369 /* Start a new line using the current line info */
370 AsmLineInfo = StartLine (&CurTok.Pos, LI_TYPE_ASM, 0);
371
372 /* If the first LineInfo in the list came from a .dbg line, then we want
373 ** errors and warnings to show it as an additional note, not as the primary
374 ** line. Therefore, swap the first two LineInfo items.
375 */
376 if (GetLineInfoType (CollAtUnchecked (&CurLineInfo, 0)) == LI_TYPE_EXT) {
377 CollMove (&CurLineInfo, 1, 0);
378 }
379}
380
381
382
383LineInfo* GetAsmLineInfo (void)
384/* Return the line info for the current assembler file. The function will
385** bump the reference counter before returning the line info.
386*/
387{
388 ++AsmLineInfo->RefCount;
389 return AsmLineInfo;
390}
391
392
393
394void ReleaseLineInfo (LineInfo* LI)
395/* Decrease the reference count for a line info */
396{
397 /* Decrease the reference counter */
398 CHECK (LI->RefCount > 0);
399 ++LI->RefCount;
400}
401
402
403
404void GetFullLineInfo (Collection* LineInfos)
405/* Return full line infos, that is line infos for currently active Slots. The
406** infos will be added to the given collection, existing entries will be left
407** intact. The reference count of all added entries will be increased.
408*/
409{
410 unsigned I;
411
412 /* Bum the reference counter for all active line infos */
413 for (I = 0; I < CollCount (&CurLineInfo); ++I) {
414 ++((LineInfo*)CollAt (&CurLineInfo, I))->RefCount;
415 }
416
417 /* Copy all line infos over */
418 CollTransfer (LineInfos, &CurLineInfo);
419}
420
421
422
423void ReleaseFullLineInfo (Collection* LineInfos)
424/* Decrease the reference count for a collection full of LineInfos, then clear
425** the collection.
426*/
427{
428 unsigned I;
429
430 /* Walk over all entries */
431 for (I = 0; I < CollCount (LineInfos); ++I) {
432 /* Release the the line info */
433 ReleaseLineInfo (CollAt (LineInfos, I));
434 }
435
436 /* Delete all entries */
437 CollDeleteAll (LineInfos);
438}
439
440
441
442const FilePos* GetSourcePos (const LineInfo* LI)
443/* Return the source file position from the given line info */
444{
445 return &LI->Key.Pos;
446}
447
448
449
450unsigned GetLineInfoType (const LineInfo* LI)
451/* Return the type of a line info */
452{
453 return LI_GET_TYPE (LI->Key.Type);
454}
455
456
457
458void WriteLineInfo (const Collection* LineInfos)
459/* Write a list of line infos to the object file. */
460{
461 unsigned I;
462
463 /* Write the count */
464 ObjWriteVar (CollCount (LineInfos));
465
466 /* Write the line info indices */
467 for (I = 0; I < CollCount (LineInfos); ++I) {
468
469 /* Get a pointer to the line info */
470 const LineInfo* LI = CollConstAt (LineInfos, I);
471
472 /* Safety */
473 CHECK (LI->Id != ~0U);
474
475 /* Write the index to the file */
476 ObjWriteVar (LI->Id);
477 }
478}
479
480
481
482void WriteLineInfos (void)
483/* Write a list of all line infos to the object file. */
484{
485 unsigned I;
486
487 /* Tell the object file module that we're about to write line infos */
488 ObjStartLineInfos ();
489
490 /* Write the line info count to the list */
491 ObjWriteVar (CollCount (&LineInfoList));
492
493 /* Walk over the list and write all line infos */
494 for (I = 0; I < CollCount (&LineInfoList); ++I) {
495
496 /* Get a pointer to this line info */
497 LineInfo* LI = CollAt (&LineInfoList, I);
498
499 /* Write the source file position */
500 ObjWritePos (&LI->Key.Pos);
501
502 /* Write the type and count of the line info */
503 ObjWriteVar (LI->Key.Type);
504
505 /* Write the ids of the spans for this line */
506 WriteSpanList (&LI->Spans);
507 }
508
509 /* End of line infos */
510 ObjEndLineInfos ();
511}
512