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