* alpha.c: Add missing prototypes.
[external/binutils.git] / gprof / tahoe.c
1 /*
2  * Copyright (c) 1983, 2001 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that: (1) source distributions retain this entire copyright
7  * notice and comment, and (2) distributions including binaries display
8  * the following acknowledgement:  ``This product includes software
9  * developed by the University of California, Berkeley and its contributors''
10  * in the documentation or other materials provided with the distribution
11  * and in all advertising materials mentioning features or use of this
12  * software. Neither the name of the University nor the names of its
13  * contributors may be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  */
19 #include "gprof.h"
20 #include "cg_arcs.h"
21 #include "corefile.h"
22 #include "hist.h"
23 #include "symtab.h"
24
25     /*
26      *        opcode of the `callf' instruction
27      */
28 #define CALLF   0xfe
29
30     /*
31      *        register for pc relative addressing
32      */
33 #define PC      0xf
34
35 enum tahoe_opermodes
36   {
37     literal, indexed, reg, regdef, autodec, autoinc, autoincdef,
38     bytedisp, bytedispdef, worddisp, worddispdef, longdisp, longdispdef,
39     immediate, absolute, byterel, bytereldef, wordrel, wordreldef,
40     longrel, longreldef
41   };
42 typedef enum tahoe_opermodes tahoe_operandenum;
43
44 /*
45  * A symbol to be the child of indirect callf:
46  */
47 Sym indirectchild;
48
49 tahoe_operandenum tahoe_operandmode PARAMS ((unsigned char *));
50 char *tahoe_operandname PARAMS ((tahoe_operandenum));
51 long tahoe_operandlength PARAMS ((unsigned char *));
52 bfd_vma tahoe_reladdr PARAMS ((char *));
53 void tahoe_find_call PARAMS ((Sym *, bfd_vma, bfd_vma));
54
55 tahoe_operandenum
56 tahoe_operandmode (modep)
57      unsigned char *modep;
58 {
59   long usesreg = ((long) *modep) & 0xf;
60
61   switch (((long) *modep) >> 4)
62     {
63     case 0:
64     case 1:
65     case 2:
66     case 3:
67       return literal;
68     case 4:
69       return indexed;
70     case 5:
71       return reg;
72     case 6:
73       return regdef;
74     case 7:
75       return autodec;
76     case 8:
77       return usesreg != 0xe ? autoinc : immediate;
78     case 9:
79       return usesreg != PC ? autoincdef : absolute;
80     case 10:
81       return usesreg != PC ? bytedisp : byterel;
82     case 11:
83       return usesreg != PC ? bytedispdef : bytereldef;
84     case 12:
85       return usesreg != PC ? worddisp : wordrel;
86     case 13:
87       return usesreg != PC ? worddispdef : wordreldef;
88     case 14:
89       return usesreg != PC ? longdisp : longrel;
90     case 15:
91       return usesreg != PC ? longdispdef : longreldef;
92     }
93   /* NOTREACHED */
94   abort ();
95 }
96
97 char *
98 tahoe_operandname (mode)
99      tahoe_operandenum mode;
100 {
101
102   switch (mode)
103     {
104     case literal:
105       return "literal";
106     case indexed:
107       return "indexed";
108     case reg:
109       return "register";
110     case regdef:
111       return "register deferred";
112     case autodec:
113       return "autodecrement";
114     case autoinc:
115       return "autoincrement";
116     case autoincdef:
117       return "autoincrement deferred";
118     case bytedisp:
119       return "byte displacement";
120     case bytedispdef:
121       return "byte displacement deferred";
122     case byterel:
123       return "byte relative";
124     case bytereldef:
125       return "byte relative deferred";
126     case worddisp:
127       return "word displacement";
128     case worddispdef:
129       return "word displacement deferred";
130     case wordrel:
131       return "word relative";
132     case wordreldef:
133       return "word relative deferred";
134     case immediate:
135       return "immediate";
136     case absolute:
137       return "absolute";
138     case longdisp:
139       return "long displacement";
140     case longdispdef:
141       return "long displacement deferred";
142     case longrel:
143       return "long relative";
144     case longreldef:
145       return "long relative deferred";
146     }
147   /* NOTREACHED */
148   abort ();
149 }
150
151 long
152 tahoe_operandlength (modep)
153      unsigned char *modep;
154 {
155
156   switch (tahoe_operandmode (modep))
157     {
158     case literal:
159     case reg:
160     case regdef:
161     case autodec:
162     case autoinc:
163     case autoincdef:
164       return 1;
165     case bytedisp:
166     case bytedispdef:
167     case byterel:
168     case bytereldef:
169       return 2;
170     case worddisp:
171     case worddispdef:
172     case wordrel:
173     case wordreldef:
174       return 3;
175     case immediate:
176     case absolute:
177     case longdisp:
178     case longdispdef:
179     case longrel:
180     case longreldef:
181       return 5;
182     case indexed:
183       return 1 + tahoe_operandlength (modep + 1);
184     }
185   /* NOTREACHED */
186   abort ();
187 }
188
189 bfd_vma
190 tahoe_reladdr (modep)
191      char *modep;
192 {
193   tahoe_operandenum mode = tahoe_operandmode (modep);
194   char *cp;
195   short *sp;
196   long *lp;
197   int i;
198   long value = 0;
199
200   cp = modep;
201   ++cp;                         /* skip over the mode */
202   switch (mode)
203     {
204     default:
205       fprintf (stderr, "[reladdr] not relative address\n");
206       return (bfd_vma) modep;
207     case byterel:
208       return (bfd_vma) (cp + sizeof *cp + *cp);
209     case wordrel:
210       for (i = 0; (size_t) i < sizeof *sp; i++)
211         value = (value << 8) + (cp[i] & 0xff);
212       return (bfd_vma) (cp + sizeof *sp + value);
213     case longrel:
214       for (i = 0; (size_t) i < sizeof *lp; i++)
215         value = (value << 8) + (cp[i] & 0xff);
216       return (bfd_vma) (cp + sizeof *lp + value);
217     }
218 }
219
220 void
221 tahoe_find_call (parent, p_lowpc, p_highpc)
222      Sym *parent;
223      bfd_vma p_lowpc;
224      bfd_vma p_highpc;
225 {
226   unsigned char *instructp;
227   long length;
228   Sym *child;
229   tahoe_operandenum mode;
230   tahoe_operandenum firstmode;
231   bfd_vma destpc;
232   static bool inited = FALSE;
233
234   if (!inited)
235     {
236       inited = TRUE;
237       sym_init (&indirectchild);
238       indirectchild.cg.prop.fract = 1.0;
239       indirectchild.cg.cyc.head = &indirectchild;
240     }
241
242   if (core_text_space == 0)
243     {
244       return;
245     }
246   if (p_lowpc < s_lowpc)
247     {
248       p_lowpc = s_lowpc;
249     }
250   if (p_highpc > s_highpc)
251     {
252       p_highpc = s_highpc;
253     }
254   DBG (CALLDEBUG, printf ("[findcall] %s: 0x%lx to 0x%lx\n",
255                           parent->name, (unsigned long) p_lowpc,
256                           (unsigned long) p_highpc));
257   for (instructp = (unsigned char *) core_text_space + p_lowpc;
258        instructp < (unsigned char *) core_text_space + p_highpc;
259        instructp += length)
260     {
261       length = 1;
262       if (*instructp == CALLF)
263         {
264           /*
265            *    maybe a callf, better check it out.
266            *      skip the count of the number of arguments.
267            */
268           DBG (CALLDEBUG, printf ("[findcall]\t0x%lx:callf",
269                                   ((unsigned long)
270                                    (instructp
271                                     - (unsigned char *) core_text_space))));
272           firstmode = tahoe_operandmode (instructp + length);
273           switch (firstmode)
274             {
275             case literal:
276             case immediate:
277               break;
278             default:
279               goto botched;
280             }
281           length += tahoe_operandlength (instructp + length);
282           mode = tahoe_operandmode (instructp + length);
283           DBG (CALLDEBUG,
284                printf ("\tfirst operand is %s", tahoe_operandname (firstmode));
285                printf ("\tsecond operand is %s\n", tahoe_operandname (mode));
286             );
287           switch (mode)
288             {
289             case regdef:
290             case bytedispdef:
291             case worddispdef:
292             case longdispdef:
293             case bytereldef:
294             case wordreldef:
295             case longreldef:
296               /*
297                *    indirect call: call through pointer
298                *      either  *d(r)   as a parameter or local
299                *              (r)     as a return value
300                *              *f      as a global pointer
301                *      [are there others that we miss?,
302                *       e.g. arrays of pointers to functions???]
303                */
304               arc_add (parent, &indirectchild, (unsigned long) 0);
305               length += tahoe_operandlength (instructp + length);
306               continue;
307             case byterel:
308             case wordrel:
309             case longrel:
310               /*
311                *    regular pc relative addressing
312                *      check that this is the address of
313                *      a function.
314                */
315               destpc = tahoe_reladdr (instructp + length)
316                 - (bfd_vma) core_text_space;
317               if (destpc >= s_lowpc && destpc <= s_highpc)
318                 {
319                   child = sym_lookup (&symtab, destpc);
320                   DBG (CALLDEBUG,
321                        printf ("[findcall]\tdestpc 0x%lx",
322                                (unsigned long) destpc);
323                        printf (" child->name %s", child->name);
324                        printf (" child->addr 0x%lx\n",
325                                (unsigned long) child->addr);
326                     );
327                   if (child->addr == destpc)
328                     {
329                       /*
330                        *    a hit
331                        */
332                       arc_add (parent, child, (unsigned long) 0);
333                       length += tahoe_operandlength (instructp + length);
334                       continue;
335                     }
336                   goto botched;
337                 }
338               /*
339                *    else:
340                *      it looked like a callf,
341                *      but it wasn't to anywhere.
342                */
343               goto botched;
344             default:
345             botched:
346               /*
347                *    something funny going on.
348                */
349               DBG (CALLDEBUG, printf ("[findcall]\tbut it's a botch\n"));
350               length = 1;
351               continue;
352             }
353         }
354     }
355 }