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