1 /*
2 * Copyright (c) 2002-2018, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 /** \file
19 * \brief Module to support assembly file annotation ( -Manno switch).
20 */
21
22 #include "gbldefs.h"
23 #include "error.h"
24 #include "global.h"
25 #include "symtab.h" /* prerequisite for ili.h */
26 #include "ili.h"
27 #include "asm_anno.h"
28 #include "fih.h"
29
30 extern char *comment_char; /* assem.c */
31
32 /** \brief Assembly block annotation */
33 typedef struct ANNO_S {
34 struct ANNO_S *next;
35 int lineno;
36 int module; /** \brief module number, 0 .. n-1 */
37 long fileloc; /** \brief Byte offset from begin of file */
38 int bihd; /** \brief bihd associated with this line */
39 int tmp; /** \brief index holder */
40 char *filename; /** \brief Name of file for this lineno */
41 FILE *fd; /** \brief file descriptor associated with this lineno */
42 } ANNO;
43
44 extern void annomod_init(void);
45 static ANNO *annomod_initx(ANNO *ahead);
46 static int qs_anno(const void *p1, const void *p2);
47 extern void annomod_asm(int blkno);
48 extern void anno_blkcnt(int blkno);
49 extern void annomod_end(void);
50 static char *get_anno_line(FILE *fptr);
51 static void put_anno_line(char *p);
52 static int find_idx(ANNO *, int, int, int);
53 static void emit_str(char *p, int lptr);
54 static void dmp_all_anno(ANNO *, FILE *, int);
55 static void dmp_anno(ANNO *, FILE *);
56
57 #define ALEN 256
58 static struct {
59 FILE *fp; /* Current file pointer */
60 FILE *fname; /* Current file name */
61 int curlin; /* Current line number */
62 INT curpos; /* Current file position. Good after each output */
63 char buf[ALEN + 4];
64 } lanno = {0};
65
66 static FILE *fanno = NULL;
67 static ANNO *amod = NULL;
68 static ANNO *ahead = NULL;
69 static int modcnt = -1;
70
71 #undef SOURCE_FILE
72 #define SOURCE_FILE gbl.file_name
73
74 extern void
annomod_init(void)75 annomod_init(void)
76 {
77 modcnt++;
78 amod = annomod_initx(ahead);
79 if (amod != NULL && modcnt == 0)
80 ahead = amod;
81 }
82
83 /** \brief Initiate annotation of the assembly file.
84 *
85 * This is called before code is generated. Augment the current annotation
86 * chain with a sorted list of the new linenumber records (ANNO) for this
87 * module.
88 *
89 * \param ahead head of annotation list
90 * \return new head of annotation list
91 */
92 static ANNO *
annomod_initx(ANNO * ahead)93 annomod_initx(ANNO *ahead)
94 {
95 static ANNO *static_ahead;
96 static int lastcnt = 0;
97 static INT file_pos = 0;
98
99 ANNO *aptr, *aptr1, *anptr;
100 int cnt = 0;
101 int cnt1 = 0;
102 int bihd, i, j;
103 int old_line_num;
104 int first_bih;
105 char *p;
106
107 /* *************************************************** *
108 * loop thru all of the ili blocks and setup line info
109 * *************************************************** */
110
111 /* ********
112 * step 1:
113 * open source file.
114 * count the number of non-zero blocks
115 * allocate and zero first ANNO node.
116 * chain to old ANNO chain.
117 */
118
119 if (modcnt == 0) {
120 if ((fanno = fopen(SOURCE_FILE, "rb")) == NULL) {
121 error((error_code_t)2, ERR_Warning, 0, SOURCE_FILE, CNULL);
122 return ahead;
123 }
124 } else if (fanno == NULL)
125 return ahead; /* don't process if src not open */
126
127 for (bihd = BIH_NEXT(0), cnt = 0; bihd; bihd = BIH_NEXT(bihd)) {
128 if (BIH_LINENO(bihd))
129 cnt++;
130 }
131
132 if (cnt == 0)
133 return NULL;
134
135 if (cnt > lastcnt) { /* get a new list after freeing old list */
136 if (static_ahead)
137 FREE(static_ahead);
138 NEW(static_ahead, ANNO, cnt);
139 BZERO(static_ahead, ANNO, cnt);
140 lastcnt = cnt;
141 } else /* Just zero out the current chain */
142 BZERO(static_ahead, ANNO, lastcnt);
143
144 anptr = aptr = ahead = static_ahead;
145
146 /* anptr should point to tail of list */
147
148 /* ********
149 * step 2:
150 * walk BIH list and chain non-zero lineno blocks
151 * into ANNO chain. There are 'cnt' non-0 lineno
152 * blocks.
153 */
154 aptr1 = aptr;
155 first_bih = -1;
156
157 for (bihd = BIH_NEXT(0), cnt1 = 0; bihd; bihd = BIH_NEXT(bihd)) {
158
159 if (BIH_LINENO(bihd)) {
160 if (first_bih == -1)
161 first_bih = bihd;
162
163 /* per flyspray 15590, check if this is inline block, get line number
164 from fihb.stg_base[].lineno. Inliner has kept a good record of
165 line numbers. Otherwise, an error lines too long will occurs
166 because p = NULL, a return from get_anno_line() when line number
167 in BIH_LINENO(bihd) is larger than line number in compile file.
168 Note: Include does not yet work correctly especially for c++ .
169 */
170 if (FIH_INL(BIH_FINDEX(bihd))) {
171 /* Go to main source file of this */
172 for (j = BIH_FINDEX(bihd); j != 0; j = FIH_PARENT(j)) {
173 if (FIH_PARENT(j) == 1) {
174 aptr1->lineno = FIH_LINENO(j);
175 break;
176 }
177 }
178 } else {
179 aptr1->lineno = BIH_LINENO(bihd);
180 }
181
182 aptr1->bihd = bihd;
183 aptr1->module = modcnt;
184 aptr1->tmp = cnt1;
185 aptr1->next = NULL;
186 aptr1++;
187 cnt1++;
188 }
189 }
190
191 assert(cnt == cnt1, "annomod_init: cnt != cnt1 :", cnt1, ERR_Severe);
192
193 /* ********
194 * step 3:
195 * Sort the newest list into ascending lineno.
196 */
197
198 #if DEBUG
199 if (DBGBIT(16, 4)) {
200 fprintf(gbl.dbgfil,
201 "annomod_init: Pre QSORT Linear dump of anno recs: cnt: %d\n", cnt);
202 dmp_all_anno(ahead, gbl.dbgfil, cnt);
203 }
204 #endif
205
206 qsort((char *)aptr, cnt, sizeof(ANNO), qs_anno);
207
208 #if DEBUG
209 if (DBGBIT(16, 4)) {
210 fprintf(gbl.dbgfil,
211 "annomod_init: Post QSORT Linear dump of anno recs: cnt: %d\n",
212 cnt);
213 dmp_all_anno(ahead, gbl.dbgfil, cnt);
214 }
215 #endif
216 /* ********
217 * step 3a:
218 * Now go back and link the ANNO records according to index occurence.
219 */
220 {
221 int idx, i;
222
223 aptr = ahead;
224 aptr1 = NULL;
225 for (i = 0, idx = 0; i < cnt; i++) {
226
227 idx = find_idx(aptr, cnt, i, idx);
228 if (i == 0)
229 ahead = &aptr[idx];
230
231 assert(idx <= cnt && idx >= 0, "outside anno loop: i:", i, ERR_Informational);
232
233 if (aptr1 == NULL)
234 aptr1 = &aptr[idx];
235
236 aptr1->next = &aptr[idx];
237 aptr1 = &aptr[idx];
238 aptr1->next = NULL;
239 }
240 }
241
242 #if DEBUG
243 if (DBGBIT(16, 4)) {
244 fprintf(gbl.dbgfil, "annomod_init3: Linked Dump of anno recs: cnt: %d\n",
245 cnt);
246 dmp_all_anno(ahead, gbl.dbgfil, 0);
247 }
248 #endif
249
250 /* ********:
251 * step 4:
252 * At this point:
253 * o ahead points a NULL terminated linked list;
254 * o aptr points at the latest contiguous list of sorted
255 * (by ascending lineno) ANNO records.
256 * o The 'next' link chains the new ANNO chain in the order
257 * of their bih occurrence.
258 * o cnt is the number of linenumbers in aptr list.
259 * aptr[0..cnt-1]
260 *
261 * Now, get file locations for each line by streaming thru
262 * source file and recording file position for each line
263 * that corresponds to a BIH LINENO.
264 * When completed, restore file and curlin to the current
265 * positions.
266 */
267
268 if (lanno.curlin < 1)
269 lanno.curlin = 1;
270 file_pos = ftell(fanno);
271 old_line_num = lanno.curlin;
272
273 for (i = 0; i < cnt; i++) {
274 int l;
275
276 l = aptr[i].lineno;
277
278 /* per flyspray 15590, keep line the same is it is include file */
279 if (FIH_INC(BIH_FINDEX(aptr[i].bihd))) {
280 l = lanno.curlin;
281 }
282
283 while (lanno.curlin <= l) {
284 if (lanno.curlin < l) {
285 p = get_anno_line(fanno);
286 if (p == NULL) {
287 flg.anno = 0;
288 return NULL;
289 }
290 }
291
292 if (lanno.curlin == l) {
293 if (lanno.curpos < 0) {
294 #if DEBUG
295 interr("annomod_init(): cannot ftell into source for line:",
296 lanno.curlin, ERR_Warning);
297 #endif
298 return NULL;
299 }
300 aptr[i].fileloc = (long)lanno.curpos;
301 aptr[i].fd = fanno;
302 aptr[i].filename = SOURCE_FILE; /* Not alloc'd for now */
303 break;
304 }
305 }
306 }
307
308 fseek(fanno, file_pos, 0);
309 lanno.curlin = old_line_num;
310 lanno.curpos = file_pos = ftell(fanno);
311
312 #if DEBUG
313 if (DBGBIT(16, 4)) {
314 fprintf(gbl.dbgfil, "annomod_init4: Dump of Linked anno recs: cnt:%d\n",
315 cnt);
316 dmp_all_anno(ahead, gbl.dbgfil, 0);
317 fprintf(gbl.dbgfil, "annomod_init4: Dump of linear anno recs: cnt: %d\n",
318 cnt);
319 dmp_all_anno(aptr, gbl.dbgfil, cnt);
320 }
321 #endif
322
323 return aptr;
324 } /* endroutine annomod_init */
325
326 /** \brief Quicksort comparison routine for annotations.
327 *
328 * Compares source file line numbers that the blocks correspond to.
329 *
330 * \param p1 first block
331 * \param p2 second block
332 *
333 * Note: parameters are void* because that is required by qsort.
334 */
335 static int
qs_anno(const void * p1,const void * p2)336 qs_anno(const void *p1, const void *p2)
337 {
338 if (((ANNO *)p1)->lineno < ((ANNO *)p2)->lineno)
339 return (-1);
340 if (((ANNO *)p1)->lineno > ((ANNO *)p2)->lineno)
341 return (1);
342 #ifdef HOST_MSDOS
343 return 0;
344 #else
345 return (-1); /* compare equality as being 'less than' */
346 #endif
347 } /* endroutine qs_anno */
348
349 /* ----------------------------------------------------------- */
350
351 /* To annotate the PFO block count in the asm file. */
352 extern void
anno_blkcnt(int blkno)353 anno_blkcnt(int blkno)
354 {
355 fprintf(gbl.asmfil, "%s block %d execution count: %lf\n", comment_char, blkno,
356 BIH_BLKCNT(blkno));
357 }
358
359 /* ----------------------------------------------------------- */
360
361 /** \brief Annotate assembly file
362 *
363 * Just seek to file position indicated and output from there to next file
364 * position or eof.
365 */
366 extern void
annomod_asm(int blkno)367 annomod_asm(int blkno)
368 {
369 int l;
370 char *p;
371
372 if (BIH_LINENO(blkno) == 0 || amod == NULL)
373 return;
374
375 /* amod should pt to record for the code that begins with
376 this bih. annomod_asm should output code from this
377 node to the *(amod+1) if amod->next is not null. */
378
379 if (amod->bihd != blkno) {
380 #if DEBUG
381 interr("Inconsistent anno records for blkno: ", blkno, ERR_Informational);
382 #endif
383 flg.anno = 0;
384 return;
385 }
386
387 again:
388 lanno.curpos = ftell(fanno);
389 if (lanno.curpos < 0) {
390 interr("annomod_asm(): cannot ftell into source for module:", modcnt, ERR_Warning);
391 amod = NULL;
392 return;
393 }
394
395 if (lanno.curpos != amod->fileloc) {
396 if (fseek(fanno, (long)amod->fileloc, 0)) {
397 interr("annomod_asm(): cannot fseek into source for lineno:",
398 amod->lineno, ERR_Warning);
399 amod = NULL;
400 return;
401 }
402
403 goto again;
404 }
405
406 lanno.curlin = amod->lineno;
407
408 if (amod->next == NULL)
409 l = lanno.curlin + 1; /* Only go to the next line if last block */
410 else {
411 if ((amod + 1)->lineno == 0)
412 l = lanno.curlin + 1;
413 else
414 l = (amod + 1)->lineno;
415 }
416
417 emit_str("\n", 0);
418 while (lanno.curlin < l) {
419 p = get_anno_line(fanno);
420 if (p == NULL)
421 break;
422 put_anno_line(p);
423 if (lanno.curlin == l)
424 break;
425 }
426
427 amod = amod->next;
428 } /* endroutine annomod_asm() */
429
430 /* ----------------------------------------------------- */
431
432 /** \brief Finish annotate assembly file for this module.
433 *
434 * Just seek to the current file position.
435 */
436 extern void
annomod_end(void)437 annomod_end(void)
438 {
439 if (amod == NULL)
440 return;
441
442 lanno.curpos = ftell(fanno);
443 assert(lanno.curpos >= 0, "annomod_end: cannot ftell into source for module:",
444 modcnt, ERR_Warning);
445
446 amod = NULL;
447 }
448
449 /* ----------------------------------------------------- */
450
451 /** \brief Get a line from the file
452 *
453 * \param fptr file pointer
454 *
455 * \return ptr to static file buffer if enough room or return NULL if problem
456 * encountered.
457 */
458 static char *
get_anno_line(FILE * fptr)459 get_anno_line(FILE *fptr)
460 {
461 #define BLEN 80
462 char *p, *p1;
463 char lbuf[BLEN + 2];
464
465 lanno.buf[0] = '\n';
466 lanno.buf[ALEN - 1] = lanno.buf[ALEN - 2] = lanno.buf[ALEN - 3] = ' ';
467 lanno.buf[1] = '\0';
468
469 p = fgets(lanno.buf, ALEN - 2, fptr);
470 if (p && (int)strlen(lanno.buf) >= ALEN - 3 && lanno.buf[ALEN - 4] != '\n') {
471 strcat(lanno.buf, "...\n");
472 while (1) {
473 p1 = fgets(lbuf, BLEN - 2, fptr);
474 if (p1 && (int)strlen(lbuf) >= BLEN - 3 && lbuf[BLEN - 4] != '\n')
475 continue;
476 else
477 break;
478 }
479 }
480
481 if (p)
482 lanno.curlin++;
483
484 lanno.curpos = ftell(fptr);
485 return p;
486
487 } /* endroutine get_anno_line */
488
489 /** \brief Output an annotated line
490 *
491 * \param p assembly buffer to print
492 */
493 static void
put_anno_line(char * p)494 put_anno_line(char *p)
495 {
496 if (!p)
497 return;
498 emit_str(comment_char, 0);
499 emit_str(" ", 0);
500 emit_str(p, 0);
501 }
502
503 static int
find_idx(ANNO * p,int cnt,int idx,int last_idx)504 find_idx(ANNO *p, int cnt, /* # of records in linear list */
505 int idx, /* idx to search for in idx field */
506 int last_idx /* last idx found */
507 )
508 {
509 int i;
510
511 for (i = last_idx; i < cnt; i++) {
512 if (p[i].tmp == idx)
513 return i;
514 }
515
516 /* if can't find forward, start from beginning and research */
517 for (i = 0; i < cnt; i++) {
518 if (p[i].tmp == idx)
519 return i;
520 }
521
522 return -1;
523 } /* endroutine find_idx */
524
525 /** \brief Emit the string to the output file
526 *
527 * \param p is the string to output
528 * \param lptr is the label sptr used for delay br filling.
529 */
530 static void
emit_str(char * p,int lptr)531 emit_str(char *p, int lptr)
532 {
533 if (p == NULL && lptr != 0)
534 return; /* Called from sched_blkinit */
535 assert(p, "emit_str: NULL ptr to be output", 0, ERR_Informational);
536
537 if (gbl.asmfil == NULL)
538 gbl.asmfil = stdout;
539
540 if (!XBIT(96, 2) || lptr >= 0 || strcmp(p, "\twnop") != 0)
541 fputs(p, gbl.asmfil);
542
543 } /* endroutine emit_str */
544
545 /* -------------------------------------------------------- */
546
547 #if DEBUG
548 static void
dmp_all_anno(ANNO * panno,FILE * fp,int flag)549 dmp_all_anno(ANNO *panno, FILE *fp, int flag)
550 {
551 static char *msg[] = {" linked list dump ", " linear list dump "};
552 ANNO *p;
553 int cnt = 0;
554
555 fprintf(fp, "dmp_all_anno: ********** ptr:%p %s flag:%d\n", (void *)panno,
556 msg[flag != 0], flag);
557 if (flag)
558 goto dmplist;
559 p = panno;
560 while (p) {
561 dmp_anno(p, fp);
562 p = p->next;
563 cnt++;
564 }
565 goto RTN;
566
567 dmplist:
568 p = panno;
569 for (cnt = 0; cnt < flag; cnt++)
570 dmp_anno(p++, fp);
571
572 RTN:
573 fprintf(fp, "\nend dmp_all_anno: ***************************** cnt:%d\n\n",
574 cnt);
575 fflush(fp);
576 } /* endroutine dmp_all_anno */
577
578 static void
dmp_anno(ANNO * panno,FILE * fp)579 dmp_anno(ANNO *panno, FILE *fp)
580 {
581 fprintf(fp, "dmp_anno: ");
582 fprintf(fp,
583 "addr:%p lineno:%d module:%d fileloc:%lu bihd:%d next:%p idx :%d\n",
584 (void *)panno, panno->lineno, panno->module, panno->fileloc,
585 panno->bihd, (void *)panno->next, panno->tmp);
586 fflush(fp);
587 }
588 #endif /* DEBUG */
589