* src/vm/jit/linenumbertable.c (linenumbertable_create, linenumbertable_linenumber_fo...
[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 #if defined(__S390__)
43 #  define ADDR_MASK(type, x) ((type)((uintptr_t)(x) & 0x7FFFFFFF))
44 #else
45 #  define ADDR_MASK(type, x) (x)
46 #endif
47
48 /* linenumbertable_create ******************************************************
49
50    Creates the linenumber table.  We allocate an array and store the
51    linenumber entry in reverse-order, so we can search the correct
52    linenumber more easily.
53
54 *******************************************************************************/
55
56 void linenumbertable_create(jitdata *jd)
57 {
58         codeinfo                     *code;
59         codegendata                  *cd;
60         linenumbertable_t            *lnt;
61         linenumbertable_entry_t      *lnte;
62         list_t                       *l;
63         linenumbertable_list_entry_t *le;
64         uint8_t                      *pv;
65         void                         *pc;
66
67         /* Get required compiler data. */
68
69         code = jd->code;
70         cd   = jd->cd;
71
72         /* Don't allocate a linenumber table if we don't need one. */
73
74         l = cd->linenumbers;
75
76         if (l->size == 0)
77                 return;
78
79         /* Allocate the linenumber table and the entries array. */
80
81         lnt  = NEW(linenumbertable_t);
82         lnte = MNEW(linenumbertable_entry_t, l->size);
83
84 #if defined(ENABLE_STATISTICS)
85         if (opt_stat) {
86                 count_linenumbertable++;
87
88                 size_linenumbertable +=
89                         sizeof(linenumbertable_t) +
90                         sizeof(linenumbertable_entry_t) * l->size;
91         }
92 #endif
93
94         /* Fill the linenumber table. */
95
96         lnt->length  = l->size;
97         lnt->entries = lnte;
98
99         /* Fill the linenumber table entries in reverse order, so the
100            search can be forward. */
101
102         pv = ADDR_MASK(uint8_t *, code->entrypoint);
103
104         for (le = list_last_unsynced(l); le != NULL;
105                  le = list_prev_unsynced(l, le), lnte++) {
106                 /* If the entry contains an mcode pointer (normal case),
107                    resolve it (see doc/inlining_stacktrace.txt for
108                    details). */
109
110                 if (le->linenumber >= -2)
111                         pc = pv + le->mpc;
112                 else
113                         pc = (void *) le->mpc;
114
115                 /* Fill the linenumber table entry. */
116
117                 lnte->linenumber = le->linenumber;
118                 lnte->pc         = pc;
119         }
120
121         /* Store the linenumber table in the codeinfo. */
122
123         code->linenumbertable = lnt;
124 }
125
126
127 /* linenumbertable_list_entry_add **********************************************
128
129    Add a line number reference.
130
131    IN:
132       cd.............current codegen data
133       linenumber.....number of line that starts with the given mcodeptr
134
135 *******************************************************************************/
136
137 void linenumbertable_list_entry_add(codegendata *cd, int32_t linenumber)
138 {
139         linenumbertable_list_entry_t *le;
140
141         le = DNEW(linenumbertable_list_entry_t);
142
143         le->linenumber = linenumber;
144         le->mpc        = cd->mcodeptr - cd->mcodebase;
145
146         list_add_last_unsynced(cd->linenumbers, le);
147 }
148
149
150 /* linenumbertable_list_entry_add_inline_start *********************************
151
152    Add a marker to the line number table indicating the start of an
153    inlined method body. (see doc/inlining_stacktrace.txt)
154
155    IN:
156       cd ..... current codegen data
157       iptr ... the ICMD_INLINE_BODY instruction
158
159 *******************************************************************************/
160
161 void linenumbertable_list_entry_add_inline_start(codegendata *cd, instruction *iptr)
162 {
163         linenumbertable_list_entry_t *le;
164         insinfo_inline               *insinfo;
165         uintptr_t                     mpc;
166
167         le = DNEW(linenumbertable_list_entry_t);
168
169         le->linenumber = (-2); /* marks start of inlined method */
170         le->mpc        = (mpc = cd->mcodeptr - cd->mcodebase);
171
172         list_add_last_unsynced(cd->linenumbers, le);
173
174         insinfo = iptr->sx.s23.s3.inlineinfo;
175
176         insinfo->startmpc = mpc; /* store for corresponding INLINE_END */
177 }
178
179
180 /* linenumbertable_list_entry_add_inline_end ***********************************
181
182    Add a marker to the line number table indicating the end of an
183    inlined method body. (see doc/inlining_stacktrace.txt)
184
185    IN:
186       cd ..... current codegen data
187       iptr ... the ICMD_INLINE_END instruction
188
189    Note:
190       iptr->method must point to the inlined callee.
191
192 *******************************************************************************/
193
194 void linenumbertable_list_entry_add_inline_end(codegendata *cd, instruction *iptr)
195 {
196         linenumbertable_list_entry_t *le;
197         insinfo_inline               *insinfo;
198
199         insinfo = iptr->sx.s23.s3.inlineinfo;
200
201         assert(insinfo);
202
203         le = DNEW(linenumbertable_list_entry_t);
204
205         /* special entry containing the methodinfo * */
206         le->linenumber = (-3) - iptr->line;
207         le->mpc        = (uintptr_t) insinfo->method;
208
209         list_add_last_unsynced(cd->linenumbers, le);
210
211         le = DNEW(linenumbertable_list_entry_t);
212
213         /* end marker with PC of start of body */
214         le->linenumber = (-1);
215         le->mpc        = insinfo->startmpc;
216
217         list_add_last_unsynced(cd->linenumbers, le);
218 }
219
220
221 /* linenumbertable_linenumber_for_pc_intern ************************************
222
223    This function search the line number table for the line
224    corresponding to a given pc. The function recurses for inlined
225    methods.
226
227 *******************************************************************************/
228
229 static s4 linenumbertable_linenumber_for_pc_intern(methodinfo **pm, linenumbertable_entry_t *lnte, int32_t lntsize, void *pc)
230 {
231         linenumbertable_entry_t *lntinline;   /* special entry for inlined method */
232
233         pc = ADDR_MASK(void *, pc);
234
235         for (; lntsize > 0; lntsize--, lnte++) {
236                 /* Note: In case of inlining this may actually compare the pc
237                    against a methodinfo *, yielding a non-sensical
238                    result. This is no problem, however, as we ignore such
239                    entries in the switch below. This way we optimize for the
240                    common case (ie. a real pc in lntentry->pc). */
241
242                 if (pc >= lnte->pc) {
243                         /* did we reach the current line? */
244
245                         if (lnte->linenumber >= 0)
246                                 return lnte->linenumber;
247
248                         /* we found a special inline entry (see
249                            doc/inlining_stacktrace.txt for details */
250
251                         switch (lnte->linenumber) {
252                         case -1: 
253                                 /* begin of inlined method (ie. INLINE_END
254                                    instruction) */
255
256                                 lntinline = --lnte;            /* get entry with methodinfo * */
257                                 lnte--;                        /* skip the special entry      */
258                                 lntsize -= 2;
259
260                                 /* search inside the inlined method */
261
262                                 if (linenumbertable_linenumber_for_pc_intern(pm, lnte, lntsize, pc)) {
263                                         /* the inlined method contained the pc */
264
265                                         *pm = (methodinfo *) lntinline->pc;
266
267                                         assert(lntinline->linenumber <= -3);
268
269                                         return (-3) - lntinline->linenumber;
270                                 }
271
272                                 /* pc was not in inlined method, continue search.
273                                    Entries inside the inlined method will be skipped
274                                    because their lntentry->pc is higher than pc.  */
275                                 break;
276
277                         case -2: 
278                                 /* end of inlined method */
279
280                                 return 0;
281
282                                 /* default: is only reached for an -3-line entry after
283                                    a skipped -2 entry. We can safely ignore it and
284                                    continue searching.  */
285                         }
286                 }
287         }
288
289         /* PC not found. */
290
291         return 0;
292 }
293
294
295 /* linenumbertable_linenumber_for_pc *******************************************
296
297    A wrapper for linenumbertable_linenumber_for_pc_intern.
298
299    NOTE: We have a intern version because the function is called
300    recursively for inlined methods.
301
302 *******************************************************************************/
303
304 int32_t linenumbertable_linenumber_for_pc(methodinfo **pm, codeinfo *code, void *pc)
305 {
306         linenumbertable_t *lnt;
307         int32_t            linenumber;
308
309         /* Get line number table. */
310
311         lnt = code->linenumbertable;
312
313         if (lnt == NULL)
314                 return 0;
315
316         /* Get the line number. */
317
318         linenumber = linenumbertable_linenumber_for_pc_intern(pm, lnt->entries, lnt->length, pc);
319
320         return linenumber;
321 }
322
323
324 /*
325  * These are local overrides for various environment variables in Emacs.
326  * Please do not remove this and leave it at the end of the file, where
327  * Emacs will automagically detect them.
328  * ---------------------------------------------------------------------
329  * Local variables:
330  * mode: c
331  * indent-tabs-mode: t
332  * c-basic-offset: 4
333  * tab-width: 4
334  * End:
335  * vim:noexpandtab:sw=4:ts=4:
336  */