9909fc0c57641560603a77aa6aaf991e01c50a66
[cacao.git] / src / vm / jit / linenumbertable.c
1 /* src/vm/jit/linenumbertable.c - linenumber handling stuff
2
3    Copyright (C) 2007
4    CACAOVM - Verein zu Foerderung der freien virtuellen Machine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <assert.h>
29 #include <stdint.h>
30
31 #include "mm/memory.h"
32
33 #include "vm/jit/code.h"
34 #include "vm/jit/codegen-common.h"
35 #include "vm/jit/linenumbertable.h"
36
37 #if defined(ENABLE_STATISTICS)
38 # include "vmcore/options.h"
39 # include "vmcore/statistics.h"
40 #endif
41
42
43 /* linenumbertable_create ******************************************************
44
45    Creates the linenumber table.  We allocate an array and store the
46    linenumber entry in reverse-order, so we can search the correct
47    linenumber more easily.
48
49 *******************************************************************************/
50
51 void linenumbertable_create(jitdata *jd)
52 {
53         codeinfo                     *code;
54         codegendata                  *cd;
55         linenumbertable_t            *lnt;
56         linenumbertable_entry_t      *lnte;
57         list_t                       *l;
58         linenumbertable_list_entry_t *le;
59         uint8_t                      *pv;
60         void                         *pc;
61
62         /* Get required compiler data. */
63
64         code = jd->code;
65         cd   = jd->cd;
66
67         /* Don't allocate a linenumber table if we don't need one. */
68
69         l = cd->linenumbers;
70
71         if (l->size == 0)
72                 return;
73
74         /* Allocate the linenumber table and the entries array. */
75
76         lnt  = NEW(linenumbertable_t);
77         lnte = MNEW(linenumbertable_entry_t, l->size);
78
79 #if defined(ENABLE_STATISTICS)
80         if (opt_stat) {
81                 count_linenumbertable++;
82
83                 size_linenumbertable +=
84                         sizeof(linenumbertable_t) +
85                         sizeof(linenumbertable_entry_t) * l->size;
86         }
87 #endif
88
89         /* Fill the linenumber table. */
90
91         lnt->length  = l->size;
92         lnt->entries = lnte;
93
94         /* Fill the linenumber table entries in reverse order, so the
95            search can be forward. */
96
97         pv = code->entrypoint;
98
99         for (le = list_last_unsynced(l); le != NULL;
100                  le = list_prev_unsynced(l, le), lnte++) {
101                 /* If the entry contains an mcode pointer (normal case),
102                    resolve it (see doc/inlining_stacktrace.txt for
103                    details). */
104
105                 if (le->linenumber >= -2)
106                         pc = pv + le->mpc;
107                 else
108                         pc = (void *) le->mpc;
109
110                 /* Fill the linenumber table entry. */
111
112                 lnte->linenumber = le->linenumber;
113                 lnte->pc         = pc;
114         }
115
116         /* Store the linenumber table in the codeinfo. */
117
118         code->linenumbertable = lnt;
119 }
120
121
122 /* linenumbertable_list_entry_add **********************************************
123
124    Add a line number reference.
125
126    IN:
127       cd.............current codegen data
128       linenumber.....number of line that starts with the given mcodeptr
129
130 *******************************************************************************/
131
132 void linenumbertable_list_entry_add(codegendata *cd, int32_t linenumber)
133 {
134         linenumbertable_list_entry_t *le;
135
136         le = DNEW(linenumbertable_list_entry_t);
137
138         le->linenumber = linenumber;
139         le->mpc        = cd->mcodeptr - cd->mcodebase;
140
141         list_add_last_unsynced(cd->linenumbers, le);
142 }
143
144
145 /* linenumbertable_list_entry_add_inline_start *********************************
146
147    Add a marker to the line number table indicating the start of an
148    inlined method body. (see doc/inlining_stacktrace.txt)
149
150    IN:
151       cd ..... current codegen data
152       iptr ... the ICMD_INLINE_BODY instruction
153
154 *******************************************************************************/
155
156 void linenumbertable_list_entry_add_inline_start(codegendata *cd, instruction *iptr)
157 {
158         linenumbertable_list_entry_t *le;
159         insinfo_inline               *insinfo;
160         uintptr_t                     mpc;
161
162         le = DNEW(linenumbertable_list_entry_t);
163
164         le->linenumber = (-2); /* marks start of inlined method */
165         le->mpc        = (mpc = cd->mcodeptr - cd->mcodebase);
166
167         list_add_last_unsynced(cd->linenumbers, le);
168
169         insinfo = iptr->sx.s23.s3.inlineinfo;
170
171         insinfo->startmpc = mpc; /* store for corresponding INLINE_END */
172 }
173
174
175 /* linenumbertable_list_entry_add_inline_end ***********************************
176
177    Add a marker to the line number table indicating the end of an
178    inlined method body. (see doc/inlining_stacktrace.txt)
179
180    IN:
181       cd ..... current codegen data
182       iptr ... the ICMD_INLINE_END instruction
183
184    Note:
185       iptr->method must point to the inlined callee.
186
187 *******************************************************************************/
188
189 void linenumbertable_list_entry_add_inline_end(codegendata *cd, instruction *iptr)
190 {
191         linenumbertable_list_entry_t *le;
192         insinfo_inline               *insinfo;
193
194         insinfo = iptr->sx.s23.s3.inlineinfo;
195
196         assert(insinfo);
197
198         le = DNEW(linenumbertable_list_entry_t);
199
200         /* special entry containing the methodinfo * */
201         le->linenumber = (-3) - iptr->line;
202         le->mpc        = (uintptr_t) insinfo->method;
203
204         list_add_last_unsynced(cd->linenumbers, le);
205
206         le = DNEW(linenumbertable_list_entry_t);
207
208         /* end marker with PC of start of body */
209         le->linenumber = (-1);
210         le->mpc        = insinfo->startmpc;
211
212         list_add_last_unsynced(cd->linenumbers, le);
213 }
214
215
216 /* linenumbertable_linenumber_for_pc_intern ************************************
217
218    This function search the line number table for the line
219    corresponding to a given pc. The function recurses for inlined
220    methods.
221
222 *******************************************************************************/
223
224 static s4 linenumbertable_linenumber_for_pc_intern(methodinfo **pm, linenumbertable_entry_t *lnte, int32_t lntsize, void *pc)
225 {
226         linenumbertable_entry_t *lntinline;   /* special entry for inlined method */
227
228         for (; lntsize > 0; lntsize--, lnte++) {
229                 /* Note: In case of inlining this may actually compare the pc
230                    against a methodinfo *, yielding a non-sensical
231                    result. This is no problem, however, as we ignore such
232                    entries in the switch below. This way we optimize for the
233                    common case (ie. a real pc in lntentry->pc). */
234
235                 if (pc >= lnte->pc) {
236                         /* did we reach the current line? */
237
238                         if (lnte->linenumber >= 0)
239                                 return lnte->linenumber;
240
241                         /* we found a special inline entry (see
242                            doc/inlining_stacktrace.txt for details */
243
244                         switch (lnte->linenumber) {
245                         case -1: 
246                                 /* begin of inlined method (ie. INLINE_END
247                                    instruction) */
248
249                                 lntinline = --lnte;            /* get entry with methodinfo * */
250                                 lnte--;                        /* skip the special entry      */
251                                 lntsize -= 2;
252
253                                 /* search inside the inlined method */
254
255                                 if (linenumbertable_linenumber_for_pc_intern(pm, lnte, lntsize, pc)) {
256                                         /* the inlined method contained the pc */
257
258                                         *pm = (methodinfo *) lntinline->pc;
259
260                                         assert(lntinline->linenumber <= -3);
261
262                                         return (-3) - lntinline->linenumber;
263                                 }
264
265                                 /* pc was not in inlined method, continue search.
266                                    Entries inside the inlined method will be skipped
267                                    because their lntentry->pc is higher than pc.  */
268                                 break;
269
270                         case -2: 
271                                 /* end of inlined method */
272
273                                 return 0;
274
275                                 /* default: is only reached for an -3-line entry after
276                                    a skipped -2 entry. We can safely ignore it and
277                                    continue searching.  */
278                         }
279                 }
280         }
281
282         /* PC not found. */
283
284         return 0;
285 }
286
287
288 /* linenumbertable_linenumber_for_pc *******************************************
289
290    A wrapper for linenumbertable_linenumber_for_pc_intern.
291
292    NOTE: We have a intern version because the function is called
293    recursively for inlined methods.
294
295 *******************************************************************************/
296
297 int32_t linenumbertable_linenumber_for_pc(methodinfo **pm, codeinfo *code, void *pc)
298 {
299         linenumbertable_t *lnt;
300         int32_t            linenumber;
301
302         /* Get line number table. */
303
304         lnt = code->linenumbertable;
305
306         if (lnt == NULL)
307                 return 0;
308
309         /* Get the line number. */
310
311         linenumber = linenumbertable_linenumber_for_pc_intern(pm, lnt->entries, lnt->length, pc);
312
313         return linenumber;
314 }
315
316
317 /*
318  * These are local overrides for various environment variables in Emacs.
319  * Please do not remove this and leave it at the end of the file, where
320  * Emacs will automagically detect them.
321  * ---------------------------------------------------------------------
322  * Local variables:
323  * mode: c
324  * indent-tabs-mode: t
325  * c-basic-offset: 4
326  * tab-width: 4
327  * End:
328  * vim:noexpandtab:sw=4:ts=4:
329  */