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