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