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 | |
60 | static unsigned HT_GenHash (const void* Key); |
61 | /* Generate the hash over a key. */ |
62 | |
63 | static const void* HT_GetKey (const void* Entry); |
64 | /* Given a pointer to the user entry data, return a pointer to the key */ |
65 | |
66 | static 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 */ |
81 | typedef struct LineInfoKey LineInfoKey; |
82 | struct LineInfoKey { |
83 | FilePos Pos; /* File position */ |
84 | unsigned Type; /* Type/count of line info */ |
85 | }; |
86 | |
87 | /* Structure that holds line info */ |
88 | struct 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 */ |
100 | static Collection LineInfoList = STATIC_COLLECTION_INITIALIZER; |
101 | |
102 | /* Collection with currently active line infos */ |
103 | static Collection CurLineInfo = STATIC_COLLECTION_INITIALIZER; |
104 | |
105 | /* Hash table functions */ |
106 | static const HashFunctions HashFunc = { |
107 | HT_GenHash, |
108 | HT_GetKey, |
109 | HT_Compare |
110 | }; |
111 | |
112 | /* Line info hash table */ |
113 | static HashTable LineInfoTab = STATIC_HASHTABLE_INITIALIZER (1051, &HashFunc); |
114 | |
115 | /* The current assembler input line */ |
116 | static LineInfo* AsmLineInfo = 0; |
117 | |
118 | |
119 | |
120 | /*****************************************************************************/ |
121 | /* Hash table functions */ |
122 | /*****************************************************************************/ |
123 | |
124 | |
125 | |
126 | static 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 | |
138 | static 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 | |
146 | static 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 | |
177 | static 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 | |
200 | static 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 | |
214 | static 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 |
240 | static 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 | |
266 | void 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 | |
282 | void 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 | |
300 | void 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 | |
320 | LineInfo* 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 | |
351 | void 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 | |
383 | LineInfo* 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 | |
394 | void 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 | |
404 | void 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 | |
423 | void 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 | |
442 | const 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 | |
450 | unsigned 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 | |
458 | void 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 | |
482 | void 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 | |