This commit was generated by cvs2svn to track changes on a CVS vendor
[external/binutils.git] / gprof / tahoe.c
1 /*
2  * Copyright (c) 1983 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
50 tahoe_operandenum
51 tahoe_operandmode (modep)
52      unsigned char *modep;
53 {
54   long usesreg = ((long) *modep) & 0xf;
55
56   switch (((long) *modep) >> 4)
57     {
58     case 0:
59     case 1:
60     case 2:
61     case 3:
62       return literal;
63     case 4:
64       return indexed;
65     case 5:
66       return reg;
67     case 6:
68       return regdef;
69     case 7:
70       return autodec;
71     case 8:
72       return usesreg != 0xe ? autoinc : immediate;
73     case 9:
74       return usesreg != PC ? autoincdef : absolute;
75     case 10:
76       return usesreg != PC ? bytedisp : byterel;
77     case 11:
78       return usesreg != PC ? bytedispdef : bytereldef;
79     case 12:
80       return usesreg != PC ? worddisp : wordrel;
81     case 13:
82       return usesreg != PC ? worddispdef : wordreldef;
83     case 14:
84       return usesreg != PC ? longdisp : longrel;
85     case 15:
86       return usesreg != PC ? longdispdef : longreldef;
87     }
88   /* NOTREACHED */
89   abort ();
90 }
91
92 char *
93 tahoe_operandname (mode)
94      tahoe_operandenum mode;
95 {
96
97   switch (mode)
98     {
99     case literal:
100       return "literal";
101     case indexed:
102       return "indexed";
103     case reg:
104       return "register";
105     case regdef:
106       return "register deferred";
107     case autodec:
108       return "autodecrement";
109     case autoinc:
110       return "autoincrement";
111     case autoincdef:
112       return "autoincrement deferred";
113     case bytedisp:
114       return "byte displacement";
115     case bytedispdef:
116       return "byte displacement deferred";
117     case byterel:
118       return "byte relative";
119     case bytereldef:
120       return "byte relative deferred";
121     case worddisp:
122       return "word displacement";
123     case worddispdef:
124       return "word displacement deferred";
125     case wordrel:
126       return "word relative";
127     case wordreldef:
128       return "word relative deferred";
129     case immediate:
130       return "immediate";
131     case absolute:
132       return "absolute";
133     case longdisp:
134       return "long displacement";
135     case longdispdef:
136       return "long displacement deferred";
137     case longrel:
138       return "long relative";
139     case longreldef:
140       return "long relative deferred";
141     }
142   /* NOTREACHED */
143   abort ();
144 }
145
146 long
147 tahoe_operandlength (modep)
148      unsigned char *modep;
149 {
150
151   switch (tahoe_operandmode (modep))
152     {
153     case literal:
154     case reg:
155     case regdef:
156     case autodec:
157     case autoinc:
158     case autoincdef:
159       return 1;
160     case bytedisp:
161     case bytedispdef:
162     case byterel:
163     case bytereldef:
164       return 2;
165     case worddisp:
166     case worddispdef:
167     case wordrel:
168     case wordreldef:
169       return 3;
170     case immediate:
171     case absolute:
172     case longdisp:
173     case longdispdef:
174     case longrel:
175     case longreldef:
176       return 5;
177     case indexed:
178       return 1 + tahoe_operandlength (modep + 1);
179     }
180   /* NOTREACHED */
181   abort ();
182 }
183
184 bfd_vma
185 tahoe_reladdr (modep)
186      char *modep;
187 {
188   tahoe_operandenum mode = tahoe_operandmode (modep);
189   char *cp;
190   short *sp;
191   long *lp;
192   int i;
193   long value = 0;
194
195   cp = modep;
196   ++cp;                         /* skip over the mode */
197   switch (mode)
198     {
199     default:
200       fprintf (stderr, "[reladdr] not relative address\n");
201       return (bfd_vma) modep;
202     case byterel:
203       return (bfd_vma) (cp + sizeof *cp + *cp);
204     case wordrel:
205       for (i = 0; (size_t) i < sizeof *sp; i++)
206         value = (value << 8) + (cp[i] & 0xff);
207       return (bfd_vma) (cp + sizeof *sp + value);
208     case longrel:
209       for (i = 0; (size_t) i < sizeof *lp; i++)
210         value = (value << 8) + (cp[i] & 0xff);
211       return (bfd_vma) (cp + sizeof *lp + value);
212     }
213 }
214
215 void
216 tahoe_find_call (parent, p_lowpc, p_highpc)
217      Sym *parent;
218      bfd_vma p_lowpc;
219      bfd_vma p_highpc;
220 {
221   unsigned char *instructp;
222   long length;
223   Sym *child;
224   tahoe_operandenum mode;
225   tahoe_operandenum firstmode;
226   bfd_vma destpc;
227   static bool inited = FALSE;
228
229   if (!inited)
230     {
231       inited = TRUE;
232       sym_init (&indirectchild);
233       indirectchild.cg.prop.fract = 1.0;
234       indirectchild.cg.cyc.head = &indirectchild;
235     }
236
237   if (core_text_space == 0)
238     {
239       return;
240     }
241   if (p_lowpc < s_lowpc)
242     {
243       p_lowpc = s_lowpc;
244     }
245   if (p_highpc > s_highpc)
246     {
247       p_highpc = s_highpc;
248     }
249   DBG (CALLDEBUG, printf ("[findcall] %s: 0x%lx to 0x%lx\n",
250                           parent->name, (unsigned long) p_lowpc,
251                           (unsigned long) p_highpc));
252   for (instructp = (unsigned char *) core_text_space + p_lowpc;
253        instructp < (unsigned char *) core_text_space + p_highpc;
254        instructp += length)
255     {
256       length = 1;
257       if (*instructp == CALLF)
258         {
259           /*
260            *    maybe a callf, better check it out.
261            *      skip the count of the number of arguments.
262            */
263           DBG (CALLDEBUG, printf ("[findcall]\t0x%x:callf",
264                                   instructp - (unsigned char *) core_text_space));
265           firstmode = tahoe_operandmode (instructp + length);
266           switch (firstmode)
267             {
268             case literal:
269             case immediate:
270               break;
271             default:
272               goto botched;
273             }
274           length += tahoe_operandlength (instructp + length);
275           mode = tahoe_operandmode (instructp + length);
276           DBG (CALLDEBUG,
277                printf ("\tfirst operand is %s", tahoe_operandname (firstmode));
278                printf ("\tsecond operand is %s\n", tahoe_operandname (mode));
279             );
280           switch (mode)
281             {
282             case regdef:
283             case bytedispdef:
284             case worddispdef:
285             case longdispdef:
286             case bytereldef:
287             case wordreldef:
288             case longreldef:
289               /*
290                *    indirect call: call through pointer
291                *      either  *d(r)   as a parameter or local
292                *              (r)     as a return value
293                *              *f      as a global pointer
294                *      [are there others that we miss?,
295                *       e.g. arrays of pointers to functions???]
296                */
297               arc_add (parent, &indirectchild, (unsigned long) 0);
298               length += tahoe_operandlength (instructp + length);
299               continue;
300             case byterel:
301             case wordrel:
302             case longrel:
303               /*
304                *    regular pc relative addressing
305                *      check that this is the address of 
306                *      a function.
307                */
308               destpc = tahoe_reladdr (instructp + length)
309                 - (bfd_vma) core_text_space;
310               if (destpc >= s_lowpc && destpc <= s_highpc)
311                 {
312                   child = sym_lookup (&symtab, destpc);
313                   DBG (CALLDEBUG,
314                        printf ("[findcall]\tdestpc 0x%lx",
315                                (unsigned long) destpc);
316                        printf (" child->name %s", child->name);
317                        printf (" child->addr 0x%lx\n",
318                                (unsigned long) child->addr);
319                     );
320                   if (child->addr == destpc)
321                     {
322                       /*
323                        *    a hit
324                        */
325                       arc_add (parent, child, (unsigned long) 0);
326                       length += tahoe_operandlength (instructp + length);
327                       continue;
328                     }
329                   goto botched;
330                 }
331               /*
332                *    else:
333                *      it looked like a callf,
334                *      but it wasn't to anywhere.
335                */
336               goto botched;
337             default:
338             botched:
339               /*
340                *    something funny going on.
341                */
342               DBG (CALLDEBUG, printf ("[findcall]\tbut it's a botch\n"));
343               length = 1;
344               continue;
345             }
346         }
347     }
348 }