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